harishaseebat92 commited on
Commit
68c0258
·
1 Parent(s): 435a716

Added the DOWNLOAD menu in the Time Series Plot section

Browse files
em_trame.py CHANGED
@@ -697,7 +697,9 @@ def export_qpu_timeseries_csv():
697
  temp_path = tmp.name
698
 
699
  content = Path(temp_path).read_bytes()
700
- server.controller.download_file(content, filename)
 
 
701
  Path(temp_path).unlink()
702
 
703
  state.export_status_message = f"Exported QPU CSV to {filename}"
@@ -724,7 +726,9 @@ def export_qpu_timeseries_png():
724
  fig.write_image(temp_path, scale=2, width=900, height=660)
725
 
726
  content = Path(temp_path).read_bytes()
727
- server.controller.download_file(content, filename)
 
 
728
  Path(temp_path).unlink()
729
 
730
  state.export_status_message = f"Exported QPU PNG to {filename}"
@@ -755,7 +759,9 @@ def export_qpu_timeseries_html():
755
  pio.write_html(fig, file=temp_path, include_plotlyjs="cdn", full_html=True)
756
 
757
  content = Path(temp_path).read_bytes()
758
- server.controller.download_file(content, filename)
 
 
759
  Path(temp_path).unlink()
760
 
761
  state.export_status_message = f"Exported QPU HTML to {filename}"
@@ -764,6 +770,143 @@ def export_qpu_timeseries_html():
764
  finally:
765
  state.show_export_status = True
766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
767
  def setup_surface_plot_data(simulation_data, nx):
768
  global data_frames, z_scale, X_grids, Y_grids, surface_clims
769
  mask = np.arange(1, nx * nx + 1) % nx != 0
@@ -1274,7 +1417,7 @@ def _build_sim_timeseries_plotly(field_type: str, positions, nx: int, times, sim
1274
  return None
1275
 
1276
  def generate_plot():
1277
- global current_mesh
1278
  if not state.simulation_has_run:
1279
  return
1280
 
@@ -1296,6 +1439,9 @@ def generate_plot():
1296
  raise ValueError("No valid monitor positions found. Enter (x, y) pairs in [0,1] x [0,1].")
1297
  fig = _build_sim_timeseries_plotly(state.timeseries_field, positions, nx, snapshot_times, simulation_data)
1298
  if fig is not None:
 
 
 
1299
  try:
1300
  ctrl.sim_ts_update(fig)
1301
  except Exception:
@@ -2297,7 +2443,10 @@ def export_vtk():
2297
  with tempfile.NamedTemporaryFile(suffix=".vtp", delete=False) as tmp:
2298
  current_mesh.save(tmp.name)
2299
  content = Path(tmp.name).read_bytes()
2300
- server.controller.download_file(content, filename)
 
 
 
2301
  Path(tmp.name).unlink() # Clean up
2302
 
2303
  state.export_status_message = f"Exported VTK to {filename}"
@@ -2345,7 +2494,9 @@ def export_vtk_all_frames():
2345
  Path(tmp_vtp_path).unlink()
2346
 
2347
  content = Path(temp_zip_path).read_bytes()
2348
- server.controller.download_file(content, zip_filename)
 
 
2349
  Path(temp_zip_path).unlink()
2350
 
2351
  state.export_status_message = f"Exported {len(frames)} frames to {zip_filename}"
@@ -2435,7 +2586,9 @@ def export_mp4():
2435
 
2436
  # Read the file content and trigger download
2437
  content = Path(temp_path).read_bytes()
2438
- server.controller.download_file(content, filename)
 
 
2439
  Path(temp_path).unlink() # Clean up
2440
 
2441
  state.export_status_message = f"Exported MP4 to {filename}"
@@ -3104,13 +3257,13 @@ with SinglePageLayout(server) as layout:
3104
  with vuetify3.VCol(cols=7, classes="pa-1 d-flex flex-column"):
3105
  # Output Configuration (appears after simulation) – hidden for QPU
3106
  with vuetify3.VCard(v_if="simulation_has_run && backend_type !== 'QPU'", classes="mb-1", style="font-size: 0.8rem;"):
3107
- with vuetify3.VCardSubtitle("Output Configuration", classes="text-subtitle-1 text-primary", style="font-size: 0.9rem; padding: 6px 10px;"):
3108
- with vuetify3.VCardText(classes="py-1 px-2"):
3109
  with vuetify3.VRadioGroup(v_model=("output_type", "Surface Plot"), row=True, density="compact", color="primary"):
3110
- vuetify3.VRadio(label="Surface", value="Surface Plot")
3111
- vuetify3.VRadio(label="Time Series", value="Time Series Plot")
3112
  with vuetify3.VContainer(v_if="output_type === 'Surface Plot'", classes="pa-0"):
3113
- vuetify3.VSelect(v_model=("surface_field", "Ez"), items=("surface_field_options", ["Ez", "Hx", "Hy"]), label="Field Component", density="compact", color="primary")
3114
  # Replace export format dropdown and individual buttons with a single Export menu
3115
  with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end"):
3116
  with vuetify3.Template(v_slot_activator="{ props }"):
@@ -3122,7 +3275,7 @@ with SinglePageLayout(server) as layout:
3122
  vuetify3.VDivider()
3123
  vuetify3.VListItem(title="Animation (MP4)", prepend_icon="mdi-movie", click=export_mp4)
3124
  with vuetify3.VContainer(v_if="output_type === 'Time Series Plot'", classes="pa-0"):
3125
- vuetify3.VSelect(v_model=("timeseries_field", "Ez"), items=("timeseries_field_options", ["All", "Ez", "Hx", "Hy"]), label="Field Component", density="compact", color="primary")
3126
  vuetify3.VTextarea(
3127
  v_model=("timeseries_points", "(0.5, 0.5)"),
3128
  label="Monitor Position(s) (x, y) in [0,1]",
@@ -3130,6 +3283,7 @@ with SinglePageLayout(server) as layout:
3130
  rows=2,
3131
  auto_grow=True,
3132
  color="primary",
 
3133
  )
3134
  vuetify3.VAlert(
3135
  v_if="timeseries_point_info",
@@ -3140,6 +3294,14 @@ with SinglePageLayout(server) as layout:
3140
  classes="mt-1",
3141
  style="white-space: pre-line;",
3142
  )
 
 
 
 
 
 
 
 
3143
 
3144
  # Main plot area (PyVista) – keep visible for QPU until data replaces it
3145
  with vuetify3.VCard(
 
697
  temp_path = tmp.name
698
 
699
  content = Path(temp_path).read_bytes()
700
+ # Encode content as base64 for browser download
701
+ content_b64 = base64.b64encode(content).decode("ascii")
702
+ server.js_call("utils", "download", filename, f"data:text/csv;base64,{content_b64}")
703
  Path(temp_path).unlink()
704
 
705
  state.export_status_message = f"Exported QPU CSV to {filename}"
 
726
  fig.write_image(temp_path, scale=2, width=900, height=660)
727
 
728
  content = Path(temp_path).read_bytes()
729
+ # Encode content as base64 for browser download
730
+ content_b64 = base64.b64encode(content).decode("ascii")
731
+ server.js_call("utils", "download", filename, f"data:image/png;base64,{content_b64}")
732
  Path(temp_path).unlink()
733
 
734
  state.export_status_message = f"Exported QPU PNG to {filename}"
 
759
  pio.write_html(fig, file=temp_path, include_plotlyjs="cdn", full_html=True)
760
 
761
  content = Path(temp_path).read_bytes()
762
+ # Encode content as base64 for browser download
763
+ content_b64 = base64.b64encode(content).decode("ascii")
764
+ server.js_call("utils", "download", filename, f"data:text/html;base64,{content_b64}")
765
  Path(temp_path).unlink()
766
 
767
  state.export_status_message = f"Exported QPU HTML to {filename}"
 
770
  finally:
771
  state.show_export_status = True
772
 
773
+ # --- Cache for Simulator Time Series (for export) ---
774
+ sim_ts_cache = {
775
+ "fig": None,
776
+ "field": None,
777
+ }
778
+
779
+ # --- Export functions for Simulator Time Series ---
780
+
781
+ def export_sim_timeseries_csv():
782
+ """Export simulator time series data to CSV."""
783
+ try:
784
+ global simulation_data, snapshot_times
785
+ if simulation_data is None or snapshot_times is None:
786
+ raise ValueError("No simulator time series to export.")
787
+
788
+ field = state.timeseries_field or "Ez"
789
+ nx_val = int(state.nx or 0)
790
+ filename = f"sim_timeseries_{field}_nx{nx_val}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
791
+
792
+ # Parse positions from state
793
+ positions = []
794
+ pts_str = state.timeseries_points or "(0.5, 0.5)"
795
+ for m in _SAMPLE_PAIR_RE.finditer(pts_str):
796
+ x_norm, y_norm = float(m.group(1)), float(m.group(2))
797
+ px = int(round(x_norm * (nx_val - 1)))
798
+ py = int(round(y_norm * (nx_val - 1)))
799
+ positions.append((px, py))
800
+
801
+ if not positions:
802
+ positions = [(nx_val // 2, nx_val // 2)]
803
+
804
+ import csv
805
+ with tempfile.NamedTemporaryFile(mode='w', newline='', suffix=".csv", delete=False) as tmp:
806
+ writer = csv.writer(tmp)
807
+ header = ["time_s"] + [f"({px},{py})" for (px, py) in positions]
808
+ writer.writerow(header)
809
+
810
+ times = np.asarray(snapshot_times)
811
+ n_frames = simulation_data.shape[0]
812
+ for i, t in enumerate(times):
813
+ if i >= n_frames:
814
+ break
815
+ row = [t]
816
+ for (px, py) in positions:
817
+ if field == 'Ez':
818
+ val = simulation_data[i, py * nx_val + px]
819
+ elif field == 'Hx':
820
+ gw, gh = nx_val, nx_val - 1
821
+ if 0 <= px < gw and 0 <= py < gh:
822
+ block = simulation_data[i, 2*nx_val*nx_val : 3*nx_val*nx_val-nx_val].reshape(gh, gw)
823
+ val = block[py, px]
824
+ else:
825
+ val = 0.0
826
+ else: # Hy
827
+ mask = np.arange(1, nx_val * nx_val + 1) % nx_val != 0
828
+ raw_block = simulation_data[i, -nx_val*nx_val:]
829
+ gw, gh = nx_val - 1, nx_val
830
+ if 0 <= px < gw and 0 <= py < gh:
831
+ val = raw_block[mask].reshape(nx_val, nx_val - 1)[py, px]
832
+ else:
833
+ val = 0.0
834
+ row.append(val)
835
+ writer.writerow(row)
836
+ temp_path = tmp.name
837
+
838
+ content = Path(temp_path).read_bytes()
839
+ content_b64 = base64.b64encode(content).decode("ascii")
840
+ server.js_call("utils", "download", filename, f"data:text/csv;base64,{content_b64}")
841
+ Path(temp_path).unlink()
842
+
843
+ state.export_status_message = f"Exported Simulator CSV to {filename}"
844
+ except Exception as e:
845
+ state.export_status_message = f"Simulator CSV export failed: {e}"
846
+ finally:
847
+ state.show_export_status = True
848
+
849
+
850
+ def export_sim_timeseries_png():
851
+ """Export simulator time series Plotly figure as PNG."""
852
+ try:
853
+ fig = sim_ts_cache.get("fig")
854
+ field = state.timeseries_field or "Ez"
855
+ if fig is None:
856
+ raise ValueError("No simulator time series to export.")
857
+
858
+ nx_val = int(state.nx or 0)
859
+ filename = f"sim_timeseries_{field}_nx{nx_val}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
860
+
861
+ with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
862
+ temp_path = tmp.name
863
+
864
+ fig.write_image(temp_path, scale=2, width=900, height=660)
865
+
866
+ content = Path(temp_path).read_bytes()
867
+ content_b64 = base64.b64encode(content).decode("ascii")
868
+ server.js_call("utils", "download", filename, f"data:image/png;base64,{content_b64}")
869
+ Path(temp_path).unlink()
870
+
871
+ state.export_status_message = f"Exported Simulator PNG to {filename}"
872
+ except Exception as e:
873
+ msg = str(e)
874
+ if "kaleido" in msg.lower():
875
+ state.export_status_message = "Simulator PNG export failed: kaleido is not installed. Run `pip install -U kaleido`."
876
+ else:
877
+ state.export_status_message = f"Simulator PNG export failed: {e}"
878
+ finally:
879
+ state.show_export_status = True
880
+
881
+
882
+ def export_sim_timeseries_html():
883
+ """Export simulator time series Plotly figure as standalone HTML."""
884
+ try:
885
+ fig = sim_ts_cache.get("fig")
886
+ field = state.timeseries_field or "Ez"
887
+ if fig is None:
888
+ raise ValueError("No simulator time series to export.")
889
+
890
+ nx_val = int(state.nx or 0)
891
+ filename = f"sim_timeseries_{field}_nx{nx_val}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.html"
892
+
893
+ with tempfile.NamedTemporaryFile(suffix=".html", delete=False) as tmp:
894
+ temp_path = tmp.name
895
+
896
+ pio.write_html(fig, file=temp_path, include_plotlyjs="cdn", full_html=True)
897
+
898
+ content = Path(temp_path).read_bytes()
899
+ content_b64 = base64.b64encode(content).decode("ascii")
900
+ server.js_call("utils", "download", filename, f"data:text/html;base64,{content_b64}")
901
+ Path(temp_path).unlink()
902
+
903
+ state.export_status_message = f"Exported Simulator HTML to {filename}"
904
+ except Exception as e:
905
+ state.export_status_message = f"Simulator HTML export failed: {e}"
906
+ finally:
907
+ state.show_export_status = True
908
+
909
+
910
  def setup_surface_plot_data(simulation_data, nx):
911
  global data_frames, z_scale, X_grids, Y_grids, surface_clims
912
  mask = np.arange(1, nx * nx + 1) % nx != 0
 
1417
  return None
1418
 
1419
  def generate_plot():
1420
+ global current_mesh, sim_ts_cache
1421
  if not state.simulation_has_run:
1422
  return
1423
 
 
1439
  raise ValueError("No valid monitor positions found. Enter (x, y) pairs in [0,1] x [0,1].")
1440
  fig = _build_sim_timeseries_plotly(state.timeseries_field, positions, nx, snapshot_times, simulation_data)
1441
  if fig is not None:
1442
+ # Cache the figure for export
1443
+ sim_ts_cache["fig"] = fig
1444
+ sim_ts_cache["field"] = state.timeseries_field
1445
  try:
1446
  ctrl.sim_ts_update(fig)
1447
  except Exception:
 
2443
  with tempfile.NamedTemporaryFile(suffix=".vtp", delete=False) as tmp:
2444
  current_mesh.save(tmp.name)
2445
  content = Path(tmp.name).read_bytes()
2446
+ # Encode content as base64 for browser download
2447
+ content_b64 = base64.b64encode(content).decode("ascii")
2448
+ # Trigger browser download via JavaScript
2449
+ server.js_call("utils", "download", filename, f"data:application/octet-stream;base64,{content_b64}")
2450
  Path(tmp.name).unlink() # Clean up
2451
 
2452
  state.export_status_message = f"Exported VTK to {filename}"
 
2494
  Path(tmp_vtp_path).unlink()
2495
 
2496
  content = Path(temp_zip_path).read_bytes()
2497
+ # Encode content as base64 for browser download
2498
+ content_b64 = base64.b64encode(content).decode("ascii")
2499
+ server.js_call("utils", "download", zip_filename, f"data:application/zip;base64,{content_b64}")
2500
  Path(temp_zip_path).unlink()
2501
 
2502
  state.export_status_message = f"Exported {len(frames)} frames to {zip_filename}"
 
2586
 
2587
  # Read the file content and trigger download
2588
  content = Path(temp_path).read_bytes()
2589
+ # Encode content as base64 for browser download
2590
+ content_b64 = base64.b64encode(content).decode("ascii")
2591
+ server.js_call("utils", "download", filename, f"data:video/mp4;base64,{content_b64}")
2592
  Path(temp_path).unlink() # Clean up
2593
 
2594
  state.export_status_message = f"Exported MP4 to {filename}"
 
3257
  with vuetify3.VCol(cols=7, classes="pa-1 d-flex flex-column"):
3258
  # Output Configuration (appears after simulation) – hidden for QPU
3259
  with vuetify3.VCard(v_if="simulation_has_run && backend_type !== 'QPU'", classes="mb-1", style="font-size: 0.8rem;"):
3260
+ with vuetify3.VCardSubtitle("Output Configuration", classes="text-subtitle-1 text-primary", style="font-size: 0.9rem; padding: 6px 10px; font-weight: 600; color: #1A1A1A;"):
3261
+ with vuetify3.VCardText(classes="py-1 px-2", style="color: #1A1A1A;"):
3262
  with vuetify3.VRadioGroup(v_model=("output_type", "Surface Plot"), row=True, density="compact", color="primary"):
3263
+ vuetify3.VRadio(label="Surface", value="Surface Plot", style="font-weight: 500;")
3264
+ vuetify3.VRadio(label="Time Series", value="Time Series Plot", style="font-weight: 500;")
3265
  with vuetify3.VContainer(v_if="output_type === 'Surface Plot'", classes="pa-0"):
3266
+ vuetify3.VSelect(v_model=("surface_field", "Ez"), items=("surface_field_options", ["Ez", "Hx", "Hy"]), label="Field Component", density="compact", color="primary", style="font-weight: 500;")
3267
  # Replace export format dropdown and individual buttons with a single Export menu
3268
  with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end"):
3269
  with vuetify3.Template(v_slot_activator="{ props }"):
 
3275
  vuetify3.VDivider()
3276
  vuetify3.VListItem(title="Animation (MP4)", prepend_icon="mdi-movie", click=export_mp4)
3277
  with vuetify3.VContainer(v_if="output_type === 'Time Series Plot'", classes="pa-0"):
3278
+ vuetify3.VSelect(v_model=("timeseries_field", "Ez"), items=("timeseries_field_options", ["All", "Ez", "Hx", "Hy"]), label="Field Component", density="compact", color="primary", style="font-weight: 500;")
3279
  vuetify3.VTextarea(
3280
  v_model=("timeseries_points", "(0.5, 0.5)"),
3281
  label="Monitor Position(s) (x, y) in [0,1]",
 
3283
  rows=2,
3284
  auto_grow=True,
3285
  color="primary",
3286
+ style="font-weight: 500;",
3287
  )
3288
  vuetify3.VAlert(
3289
  v_if="timeseries_point_info",
 
3294
  classes="mt-1",
3295
  style="white-space: pre-line;",
3296
  )
3297
+ # DOWNLOAD menu for Simulator Time Series
3298
+ with vuetify3.VMenu(open_on_hover=True, close_on_content_click=True, location="end"):
3299
+ with vuetify3.Template(v_slot_activator="{ props }"):
3300
+ vuetify3.VBtn(v_bind="props", text="DOWNLOAD", color="primary", variant="tonal", block=True, classes="mt-1")
3301
+ with vuetify3.VList(density="compact"):
3302
+ vuetify3.VListItem(title="Download CSV", prepend_icon="mdi-download", click=export_sim_timeseries_csv)
3303
+ vuetify3.VListItem(title="Download PNG", prepend_icon="mdi-image", click=export_sim_timeseries_png)
3304
+ vuetify3.VListItem(title="Download HTML", prepend_icon="mdi-file-html", click=export_sim_timeseries_html)
3305
 
3306
  # Main plot area (PyVista) – keep visible for QPU until data replaces it
3307
  with vuetify3.VCard(
pages/__pycache__/qlbm_page.cpython-310.pyc CHANGED
Binary files a/pages/__pycache__/qlbm_page.cpython-310.pyc and b/pages/__pycache__/qlbm_page.cpython-310.pyc differ