File size: 7,201 Bytes
ed65aea
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
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
#!/usr/bin/env python3
"""
Deploy script for DR MURPHY ODE Dashboard to HuggingFace Spaces.

Run this from any directory:
    python3 /path/to/hf_ode_space/deploy.py

Steps:
  1. Copy source files from the project tree into this flat directory
  2. Patch sys.path imports for flat structure
  3. Fix run_sweep_unified argument mismatch
  4. Test import chain
  5. Deploy to HuggingFace
"""

import shutil
import os
import sys
import re

HERE = os.path.dirname(os.path.abspath(__file__))
SRC_BASE = os.path.dirname(os.path.dirname(HERE))  # cycle2mdot_acceleration/

def step1_copy_files():
    """Copy all source files into the flat deployment directory."""
    print("Step 1: Copying source files...")

    # Dashboard files
    dash = os.path.join(SRC_BASE, 'ode_reformulation/dashboard')
    for f in ['scenario_gui.py', 'scenario_analyzer.py', 'sweep_cache.py', 'pump_animation.js']:
        src = os.path.join(dash, f)
        dst = os.path.join(HERE, f)
        shutil.copy2(src, dst)
        print(f"  Copied dashboard/{f}")

    # ODE engine files
    ode = os.path.join(SRC_BASE, 'ode_reformulation')
    for f in ['cycle2mdot_ode_v2.py', 'cycle2mdot_10var_njit.py']:
        shutil.copy2(os.path.join(ode, f), os.path.join(HERE, f))
        print(f"  Copied ode_reformulation/{f}")

    # Project root files
    for f in ['cycle2mdot_fast.py', 'cycle2mdot_cached.py', 'h2_ps_lut.npz']:
        shutil.copy2(os.path.join(SRC_BASE, f), os.path.join(HERE, f))
        print(f"  Copied {f}")

    # break_coolprop directory
    bc_src = os.path.join(SRC_BASE, 'break_coolprop')
    bc_dst = os.path.join(HERE, 'break_coolprop')
    os.makedirs(bc_dst, exist_ok=True)
    for f in ['__init__.py', 'helmholtz_h2.py', 'lut_ps.py', 'h2_props.py', 'cycle2mdot_break.py']:
        shutil.copy2(os.path.join(bc_src, f), os.path.join(bc_dst, f))
        print(f"  Copied break_coolprop/{f}")

    # sweep_analysis.py is already created by the Write tool
    # engine_bridge.py, app.py, README.md, requirements.txt are already present

    print("  Done.")


def step2_patch_imports():
    """Patch sys.path hacks in copied engine files for flat structure."""
    print("\nStep 2: Patching imports for flat structure...")

    # cycle2mdot_ode_v2.py: change sys.path.insert to use current dir
    _patch_sys_path(os.path.join(HERE, 'cycle2mdot_ode_v2.py'))

    # cycle2mdot_10var_njit.py: same
    _patch_sys_path(os.path.join(HERE, 'cycle2mdot_10var_njit.py'))

    print("  Done.")


def _patch_sys_path(filepath):
    """Replace sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
    with sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))"""
    with open(filepath, 'r') as f:
        content = f.read()

    # Replace grandparent path with current directory path
    old_pattern = "sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))"
    new_pattern = "sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))"

    if old_pattern in content:
        content = content.replace(old_pattern, new_pattern)
        with open(filepath, 'w') as f:
            f.write(content)
        print(f"  Patched {os.path.basename(filepath)}: sys.path -> current dir")
    else:
        print(f"  {os.path.basename(filepath)}: no sys.path patch needed")


def step3_fix_sweep_args():
    """Fix run_sweep_unified to accept the engine argument from sr_all_inputs."""
    print("\nStep 3: Fixing run_sweep_unified argument mismatch...")

    gui_path = os.path.join(HERE, 'scenario_gui.py')
    with open(gui_path, 'r') as f:
        content = f.read()

    # Find the function signature and add engine parameter
    old_sig = "                      fill_type, vsnubber, vchss, aov140f, rodia,\n                      num_cycles=1):"
    new_sig = "                      fill_type, vsnubber, vchss, aov140f, rodia,\n                      num_cycles=1, engine='njit_10var'):"

    if old_sig in content and "def run_sweep_unified" in content:
        content = content.replace(old_sig, new_sig, 1)
        with open(gui_path, 'w') as f:
            f.write(content)
        print("  Added 'engine' parameter to run_sweep_unified()")
    else:
        print("  run_sweep_unified already has engine param or signature changed")

    print("  Done.")


def step4_test_imports():
    """Test that the import chain works in the flat structure."""
    print("\nStep 4: Testing import chain...")

    # Add this directory to path
    if HERE not in sys.path:
        sys.path.insert(0, HERE)

    try:
        import engine_bridge
        print(f"  engine_bridge: OK (engines: {engine_bridge.ENGINES})")
    except Exception as e:
        print(f"  engine_bridge: FAILED - {e}")
        return False

    try:
        import scenario_analyzer
        print("  scenario_analyzer: OK")
    except Exception as e:
        print(f"  scenario_analyzer: FAILED - {e}")
        return False

    try:
        import sweep_analysis
        print("  sweep_analysis: OK")
    except Exception as e:
        print(f"  sweep_analysis: FAILED - {e}")
        return False

    try:
        import sweep_cache
        print("  sweep_cache: OK")
    except Exception as e:
        print(f"  sweep_cache: FAILED - {e}")
        return False

    print("  All imports OK!")
    return True


def step5_deploy():
    """Deploy to HuggingFace Spaces."""
    print("\nStep 5: Deploying to HuggingFace...")

    try:
        from huggingface_hub import HfApi
    except ImportError:
        print("  huggingface_hub not installed. Installing...")
        os.system(f"{sys.executable} -m pip install huggingface_hub")
        from huggingface_hub import HfApi

    api = HfApi()

    # Create the repo (or verify it exists)
    try:
        api.create_repo("csh2/ode-pump-dashboard", repo_type="space",
                        space_sdk="gradio", exist_ok=True)
        print("  Repository csh2/ode-pump-dashboard created/verified.")
    except Exception as e:
        print(f"  Error creating repo: {e}")
        print("  You may need to run: huggingface-cli login")
        return False

    # Upload the folder (exclude deploy.py, __pycache__, .db files)
    try:
        api.upload_folder(
            folder_path=HERE,
            repo_id="csh2/ode-pump-dashboard",
            repo_type="space",
            ignore_patterns=["deploy.py", "__pycache__", "*.db", "*.db-shm", "*.db-wal",
                             ".DS_Store", "*.pyc"],
        )
        print("  Upload complete!")
        print("  Space URL: https://huggingface.co/spaces/csh2/ode-pump-dashboard")
    except Exception as e:
        print(f"  Upload failed: {e}")
        print("  You may need to run: huggingface-cli login")
        return False

    return True


def main():
    print("=" * 60)
    print("DR MURPHY ODE -- HuggingFace Space Deployment")
    print("=" * 60)

    step1_copy_files()
    step2_patch_imports()
    step3_fix_sweep_args()

    ok = step4_test_imports()
    if not ok:
        print("\nImport test failed. Fix errors before deploying.")
        sys.exit(1)

    step5_deploy()

    print("\n" + "=" * 60)
    print("Deployment complete!")
    print("=" * 60)


if __name__ == "__main__":
    main()