Spaces:
Runtime error
Runtime error
harishaseebat92 commited on
Commit ·
610d3d1
1
Parent(s): 20f8534
Upload Window : Em
Browse files- em/simulation.py +265 -1
- em/state.py +12 -0
- em/ui.py +123 -1
em/simulation.py
CHANGED
|
@@ -1436,4 +1436,268 @@ def update_value_display(point):
|
|
| 1436 |
text = f"Index: ({ix}, {iy}) | Position: ({x_code:.3f}, {y_code:.3f})\nValue: {value:.3e}"
|
| 1437 |
|
| 1438 |
plotter.add_text(text, name="value_text", position="lower_left", color="black", font_size=10)
|
| 1439 |
-
ctrl.view_update()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1436 |
text = f"Index: ({ix}, {iy}) | Position: ({x_code:.3f}, {y_code:.3f})\nValue: {value:.3e}"
|
| 1437 |
|
| 1438 |
plotter.add_text(text, name="value_text", position="lower_left", color="black", font_size=10)
|
| 1439 |
+
ctrl.view_update()
|
| 1440 |
+
|
| 1441 |
+
|
| 1442 |
+
# ---------------------------------------------------------------------------
|
| 1443 |
+
# EM Job Result Upload Processing
|
| 1444 |
+
# ---------------------------------------------------------------------------
|
| 1445 |
+
|
| 1446 |
+
def process_uploaded_em_job_result():
|
| 1447 |
+
"""
|
| 1448 |
+
Process an uploaded IBM/IonQ EM job result JSON file and generate a time-series plot.
|
| 1449 |
+
|
| 1450 |
+
This function:
|
| 1451 |
+
1. Decodes the uploaded file (base64 -> JSON)
|
| 1452 |
+
2. Parses the job result to extract field values (expectation values or counts)
|
| 1453 |
+
3. Builds time frames based on user-specified T and dt
|
| 1454 |
+
4. Generates a Plotly time-series figure
|
| 1455 |
+
|
| 1456 |
+
Required JSON structure (example):
|
| 1457 |
+
- IBM: List of expectation values or PrimitiveResult
|
| 1458 |
+
- IonQ: Dict with "field_values" or list of values
|
| 1459 |
+
- Simple format: {"field_values": [0.1, 0.2, ...], "times": [0.0, 0.1, ...]}
|
| 1460 |
+
"""
|
| 1461 |
+
import base64
|
| 1462 |
+
import json
|
| 1463 |
+
import plotly.graph_objects as go
|
| 1464 |
+
|
| 1465 |
+
if not state.bound:
|
| 1466 |
+
return
|
| 1467 |
+
|
| 1468 |
+
# Validate file upload
|
| 1469 |
+
uploaded = state.em_job_upload
|
| 1470 |
+
if not uploaded:
|
| 1471 |
+
state.em_job_upload_error = "No file uploaded. Please select a JSON file."
|
| 1472 |
+
return
|
| 1473 |
+
|
| 1474 |
+
# Reset messages
|
| 1475 |
+
state.em_job_upload_error = ""
|
| 1476 |
+
state.em_job_upload_success = ""
|
| 1477 |
+
state.em_job_is_processing = True
|
| 1478 |
+
|
| 1479 |
+
try:
|
| 1480 |
+
from .simulation import log_to_console
|
| 1481 |
+
except ImportError:
|
| 1482 |
+
def log_to_console(msg):
|
| 1483 |
+
print(msg)
|
| 1484 |
+
|
| 1485 |
+
log_to_console("Processing uploaded EM job result...")
|
| 1486 |
+
|
| 1487 |
+
try:
|
| 1488 |
+
# Handle list or single file
|
| 1489 |
+
file_data = uploaded[0] if isinstance(uploaded, list) else uploaded
|
| 1490 |
+
|
| 1491 |
+
# Get filename for display
|
| 1492 |
+
filename = file_data.get("name", "unknown.json") if isinstance(file_data, dict) else "unknown.json"
|
| 1493 |
+
state.em_job_upload_filename = filename
|
| 1494 |
+
log_to_console(f"Processing file: {filename}")
|
| 1495 |
+
|
| 1496 |
+
# Decode content
|
| 1497 |
+
content = file_data.get("content", "") if isinstance(file_data, dict) else ""
|
| 1498 |
+
|
| 1499 |
+
if isinstance(content, bytes):
|
| 1500 |
+
json_str = content.decode("utf-8")
|
| 1501 |
+
else:
|
| 1502 |
+
if content.startswith("data:"):
|
| 1503 |
+
content = content.split(",", 1)[1]
|
| 1504 |
+
raw_bytes = base64.b64decode(content)
|
| 1505 |
+
json_str = raw_bytes.decode("utf-8")
|
| 1506 |
+
|
| 1507 |
+
# Parse parameters from UI
|
| 1508 |
+
field_type = str(state.em_job_field_type or "Ez").strip()
|
| 1509 |
+
|
| 1510 |
+
# Parse monitor point tuple string "(x, y)"
|
| 1511 |
+
monitor_point_str = str(state.em_job_monitor_point or "(0, 0)").strip()
|
| 1512 |
+
try:
|
| 1513 |
+
# Remove parentheses and split by comma
|
| 1514 |
+
cleaned = monitor_point_str.strip("() ")
|
| 1515 |
+
parts = [p.strip() for p in cleaned.split(",")]
|
| 1516 |
+
monitor_x = int(parts[0]) if len(parts) > 0 else 0
|
| 1517 |
+
monitor_y = int(parts[1]) if len(parts) > 1 else 0
|
| 1518 |
+
except (ValueError, IndexError):
|
| 1519 |
+
monitor_x, monitor_y = 0, 0
|
| 1520 |
+
|
| 1521 |
+
total_time = float(state.em_job_total_time or 1.0)
|
| 1522 |
+
snapshot_dt = float(state.em_job_snapshot_dt or 0.1)
|
| 1523 |
+
nx = int(state.em_job_nx or 4)
|
| 1524 |
+
platform = str(state.em_job_platform or "IBM")
|
| 1525 |
+
|
| 1526 |
+
log_to_console(f"Parameters: field={field_type}, pos=({monitor_x},{monitor_y}), T={total_time}, dt={snapshot_dt}, nx={nx}, platform={platform}")
|
| 1527 |
+
|
| 1528 |
+
# Parse JSON
|
| 1529 |
+
result = json.loads(json_str)
|
| 1530 |
+
|
| 1531 |
+
# Extract field values based on result structure
|
| 1532 |
+
field_values = []
|
| 1533 |
+
times = []
|
| 1534 |
+
|
| 1535 |
+
# Try different result formats
|
| 1536 |
+
if isinstance(result, dict):
|
| 1537 |
+
# Format 1: {"field_values": [...], "times": [...]}
|
| 1538 |
+
if "field_values" in result:
|
| 1539 |
+
field_values = result["field_values"]
|
| 1540 |
+
times = result.get("times", None)
|
| 1541 |
+
log_to_console(f"Found 'field_values' key with {len(field_values)} values")
|
| 1542 |
+
|
| 1543 |
+
# Format 2: {"results": {...}} with key -> value mapping
|
| 1544 |
+
elif "results" in result:
|
| 1545 |
+
results_dict = result["results"]
|
| 1546 |
+
# Sort by key (time index) and extract values
|
| 1547 |
+
sorted_keys = sorted(results_dict.keys(), key=lambda x: float(x) if x.replace('.','').isdigit() else 0)
|
| 1548 |
+
field_values = [results_dict[k] for k in sorted_keys]
|
| 1549 |
+
log_to_console(f"Found 'results' dict with {len(field_values)} entries")
|
| 1550 |
+
|
| 1551 |
+
# Format 3: {"expectation_values": [...]}
|
| 1552 |
+
elif "expectation_values" in result:
|
| 1553 |
+
exp_vals = result["expectation_values"]
|
| 1554 |
+
# Convert expectation values to field values: sqrt((1 - z_exp) / 2)
|
| 1555 |
+
field_values = [np.sqrt((1 - float(z)) / 2) for z in exp_vals]
|
| 1556 |
+
log_to_console(f"Found 'expectation_values' with {len(field_values)} values, converted to field values")
|
| 1557 |
+
|
| 1558 |
+
# Format 4: IBM RuntimeEncoder result with nested structure
|
| 1559 |
+
elif "pub_results" in result or "results" in result:
|
| 1560 |
+
pub_results = result.get("pub_results", result.get("results", []))
|
| 1561 |
+
for pr in pub_results:
|
| 1562 |
+
if isinstance(pr, dict) and "data" in pr:
|
| 1563 |
+
data = pr["data"]
|
| 1564 |
+
if "evs" in data:
|
| 1565 |
+
z_exp = float(data["evs"])
|
| 1566 |
+
field_values.append(np.sqrt((1 - z_exp) / 2))
|
| 1567 |
+
log_to_console(f"Parsed {len(field_values)} values from pub_results")
|
| 1568 |
+
|
| 1569 |
+
else:
|
| 1570 |
+
# Try to use the dict values directly if they look numeric
|
| 1571 |
+
if all(isinstance(v, (int, float)) for v in result.values()):
|
| 1572 |
+
sorted_keys = sorted(result.keys())
|
| 1573 |
+
field_values = [result[k] for k in sorted_keys]
|
| 1574 |
+
log_to_console(f"Using dict values directly: {len(field_values)} values")
|
| 1575 |
+
|
| 1576 |
+
elif isinstance(result, list):
|
| 1577 |
+
# Format 5: Simple list of values
|
| 1578 |
+
if all(isinstance(v, (int, float)) for v in result):
|
| 1579 |
+
field_values = result
|
| 1580 |
+
log_to_console(f"Simple list with {len(field_values)} values")
|
| 1581 |
+
|
| 1582 |
+
# Format 6: List of dicts (e.g., IBM PrimitiveResult-like)
|
| 1583 |
+
elif all(isinstance(v, dict) for v in result):
|
| 1584 |
+
for item in result:
|
| 1585 |
+
if "evs" in item:
|
| 1586 |
+
z_exp = float(item["evs"])
|
| 1587 |
+
field_values.append(np.sqrt((1 - z_exp) / 2))
|
| 1588 |
+
elif "value" in item:
|
| 1589 |
+
field_values.append(float(item["value"]))
|
| 1590 |
+
elif "data" in item and isinstance(item["data"], dict):
|
| 1591 |
+
if "evs" in item["data"]:
|
| 1592 |
+
z_exp = float(item["data"]["evs"])
|
| 1593 |
+
field_values.append(np.sqrt((1 - z_exp) / 2))
|
| 1594 |
+
log_to_console(f"Parsed {len(field_values)} values from list of dicts")
|
| 1595 |
+
|
| 1596 |
+
if not field_values:
|
| 1597 |
+
state.em_job_upload_error = "Could not extract field values from JSON. Check file format."
|
| 1598 |
+
state.em_job_is_processing = False
|
| 1599 |
+
return
|
| 1600 |
+
|
| 1601 |
+
# Generate times if not provided
|
| 1602 |
+
if not times:
|
| 1603 |
+
# Use create_time_frames from delta_impulse_generator
|
| 1604 |
+
try:
|
| 1605 |
+
times = create_time_frames(total_time, snapshot_dt)
|
| 1606 |
+
except:
|
| 1607 |
+
# Fallback: generate linearly
|
| 1608 |
+
num_steps = len(field_values)
|
| 1609 |
+
times = [i * snapshot_dt for i in range(num_steps)]
|
| 1610 |
+
|
| 1611 |
+
# Ensure times matches field_values length
|
| 1612 |
+
if len(times) != len(field_values):
|
| 1613 |
+
log_to_console(f"Warning: times ({len(times)}) != field_values ({len(field_values)}), regenerating times")
|
| 1614 |
+
num_steps = len(field_values)
|
| 1615 |
+
times = [i * snapshot_dt for i in range(num_steps)]
|
| 1616 |
+
|
| 1617 |
+
log_to_console(f"Building time-series plot: {len(field_values)} points")
|
| 1618 |
+
|
| 1619 |
+
# Build Plotly figure
|
| 1620 |
+
fig = go.Figure()
|
| 1621 |
+
|
| 1622 |
+
# Determine grid dimensions for label
|
| 1623 |
+
if field_type == 'Ez':
|
| 1624 |
+
gw, gh = nx, nx
|
| 1625 |
+
elif field_type == 'Hx':
|
| 1626 |
+
gw, gh = nx, nx - 1
|
| 1627 |
+
else:
|
| 1628 |
+
gw, gh = nx - 1, nx
|
| 1629 |
+
|
| 1630 |
+
from .utils import normalized_position_label
|
| 1631 |
+
label = normalized_position_label(monitor_x, monitor_y, gw, gh)
|
| 1632 |
+
|
| 1633 |
+
# Color based on field type
|
| 1634 |
+
if field_type == 'Ez':
|
| 1635 |
+
color = "#d32f2f" # Red
|
| 1636 |
+
elif field_type == 'Hx':
|
| 1637 |
+
color = "#388e3c" # Green
|
| 1638 |
+
else:
|
| 1639 |
+
color = "#1976d2" # Blue
|
| 1640 |
+
|
| 1641 |
+
fig.add_trace(
|
| 1642 |
+
go.Scatter(
|
| 1643 |
+
x=list(times),
|
| 1644 |
+
y=[float(v) for v in field_values],
|
| 1645 |
+
mode='lines+markers',
|
| 1646 |
+
name=f"{field_type} @ {label}",
|
| 1647 |
+
line=dict(color=color, width=2.5),
|
| 1648 |
+
marker=dict(size=7, symbol="circle", color=color),
|
| 1649 |
+
hovertemplate=f"{field_type} | t=%{{x:.3f}}s<br>Value=%{{y:.6g}}<extra>{label}</extra>",
|
| 1650 |
+
)
|
| 1651 |
+
)
|
| 1652 |
+
|
| 1653 |
+
max_abs = max((abs(float(v)) for v in field_values), default=1.0)
|
| 1654 |
+
pad = 0.12 * max_abs if max_abs > 0 else 0.1
|
| 1655 |
+
|
| 1656 |
+
fig.update_layout(
|
| 1657 |
+
title=f"{platform} QPU Time Series (Uploaded) - {field_type} @ {label}",
|
| 1658 |
+
height=660, width=900,
|
| 1659 |
+
margin=dict(l=50, r=30, t=50, b=50),
|
| 1660 |
+
hovermode="x unified",
|
| 1661 |
+
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1, title_text=""),
|
| 1662 |
+
paper_bgcolor="#FFFFFF",
|
| 1663 |
+
plot_bgcolor="#FFFFFF",
|
| 1664 |
+
)
|
| 1665 |
+
fig.update_xaxes(title_text="Time (s)", title_font=dict(size=22), tickfont=dict(size=16), showgrid=True, gridcolor="rgba(0,0,0,.06)")
|
| 1666 |
+
fig.update_yaxes(title_text="Field Value", title_font=dict(size=22), tickfont=dict(size=16), showgrid=True, gridcolor="rgba(0,0,0,.06)")
|
| 1667 |
+
fig.update_yaxes(range=[-max_abs - pad, max_abs + pad])
|
| 1668 |
+
|
| 1669 |
+
# Cache the figure for export
|
| 1670 |
+
qpu_ts_cache["fig"] = fig
|
| 1671 |
+
qpu_ts_cache["times"] = list(times)
|
| 1672 |
+
qpu_ts_cache["series_map"] = {(field_type, monitor_x, monitor_y): list(field_values)}
|
| 1673 |
+
qpu_ts_cache["field"] = field_type
|
| 1674 |
+
qpu_ts_cache["unique_fields"] = [field_type]
|
| 1675 |
+
|
| 1676 |
+
# Update the Plotly figure widget
|
| 1677 |
+
try:
|
| 1678 |
+
ctrl.qpu_ts_update(fig)
|
| 1679 |
+
except Exception:
|
| 1680 |
+
pass
|
| 1681 |
+
|
| 1682 |
+
# Update state
|
| 1683 |
+
state.simulation_has_run = True
|
| 1684 |
+
state.qpu_ts_ready = True
|
| 1685 |
+
state.qpu_plot_style = "width: 900px; height: 660px; margin: 0 auto;"
|
| 1686 |
+
state.qpu_plot_field_options = ["All", field_type]
|
| 1687 |
+
state.qpu_plot_filter = "All"
|
| 1688 |
+
state.qpu_plot_position_options = ["All positions", label]
|
| 1689 |
+
state.qpu_plot_position_filter = "All positions"
|
| 1690 |
+
|
| 1691 |
+
state.em_job_upload_success = f"✓ Successfully processed {len(field_values)} time step(s) from {filename}"
|
| 1692 |
+
log_to_console(f"Upload processing complete: {len(field_values)} points plotted")
|
| 1693 |
+
|
| 1694 |
+
except json.JSONDecodeError as e:
|
| 1695 |
+
state.em_job_upload_error = f"Invalid JSON file: {e}"
|
| 1696 |
+
log_to_console(f"JSON decode error: {e}")
|
| 1697 |
+
except Exception as e:
|
| 1698 |
+
state.em_job_upload_error = f"Error processing job result: {e}"
|
| 1699 |
+
log_to_console(f"Processing error: {e}")
|
| 1700 |
+
import traceback
|
| 1701 |
+
log_to_console(traceback.format_exc())
|
| 1702 |
+
finally:
|
| 1703 |
+
state.em_job_is_processing = False
|
em/state.py
CHANGED
|
@@ -297,6 +297,18 @@ def _init_state_defaults():
|
|
| 297 |
"simulation_progress": 0,
|
| 298 |
"show_progress": False,
|
| 299 |
"console_logs": "Console initialized...\n",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
})
|
| 301 |
|
| 302 |
# Ensure hole snap state exists
|
|
|
|
| 297 |
"simulation_progress": 0,
|
| 298 |
"show_progress": False,
|
| 299 |
"console_logs": "Console initialized...\n",
|
| 300 |
+
# --- EM Job Upload State ---
|
| 301 |
+
"em_job_upload": None,
|
| 302 |
+
"em_job_upload_filename": "",
|
| 303 |
+
"em_job_upload_error": "",
|
| 304 |
+
"em_job_upload_success": "",
|
| 305 |
+
"em_job_is_processing": False,
|
| 306 |
+
"em_job_platform": "IBM",
|
| 307 |
+
"em_job_field_type": "Ez",
|
| 308 |
+
"em_job_monitor_point": "(0, 0)",
|
| 309 |
+
"em_job_total_time": 1.0,
|
| 310 |
+
"em_job_snapshot_dt": 0.1,
|
| 311 |
+
"em_job_nx": 4,
|
| 312 |
})
|
| 313 |
|
| 314 |
# Ensure hole snap state exists
|
em/ui.py
CHANGED
|
@@ -11,7 +11,7 @@ from .state import state, ctrl, get_server
|
|
| 11 |
from .globals import plotter, GRID_SIZES
|
| 12 |
from .geometry import build_geometry_placeholder as _build_geometry_placeholder
|
| 13 |
from .excitation import build_excitation_placeholder as _build_excitation_placeholder
|
| 14 |
-
from .simulation import run_simulation_only, reset_to_defaults, stop_simulation_handler
|
| 15 |
from .exports import (
|
| 16 |
export_vtk, export_vtk_all_frames, export_mp4,
|
| 17 |
export_sim_timeseries_csv, export_sim_timeseries_png, export_sim_timeseries_html,
|
|
@@ -103,6 +103,7 @@ def build_ui():
|
|
| 103 |
_build_backends_card()
|
| 104 |
_build_output_preferences_card()
|
| 105 |
_build_run_buttons()
|
|
|
|
| 106 |
vuetify3.VSpacer()
|
| 107 |
_build_reset_button()
|
| 108 |
|
|
@@ -490,6 +491,127 @@ def _build_output_preferences_card():
|
|
| 490 |
)
|
| 491 |
|
| 492 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
def _build_run_buttons():
|
| 494 |
"""Build the Run and Stop buttons row."""
|
| 495 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|
|
|
|
| 11 |
from .globals import plotter, GRID_SIZES
|
| 12 |
from .geometry import build_geometry_placeholder as _build_geometry_placeholder
|
| 13 |
from .excitation import build_excitation_placeholder as _build_excitation_placeholder
|
| 14 |
+
from .simulation import run_simulation_only, reset_to_defaults, stop_simulation_handler, process_uploaded_em_job_result
|
| 15 |
from .exports import (
|
| 16 |
export_vtk, export_vtk_all_frames, export_mp4,
|
| 17 |
export_sim_timeseries_csv, export_sim_timeseries_png, export_sim_timeseries_html,
|
|
|
|
| 103 |
_build_backends_card()
|
| 104 |
_build_output_preferences_card()
|
| 105 |
_build_run_buttons()
|
| 106 |
+
_build_job_upload_card()
|
| 107 |
vuetify3.VSpacer()
|
| 108 |
_build_reset_button()
|
| 109 |
|
|
|
|
| 491 |
)
|
| 492 |
|
| 493 |
|
| 494 |
+
def _build_job_upload_card():
|
| 495 |
+
"""Build the Job Result Upload card for loading previously saved QPU results."""
|
| 496 |
+
with vuetify3.VCard(classes="mb-1", style="font-size: 0.8rem;"):
|
| 497 |
+
with vuetify3.VCardTitle("Upload Job Results", classes="text-subtitle-1 text-primary", style="font-size: 0.9rem; padding: 6px 10px;"):
|
| 498 |
+
pass
|
| 499 |
+
with vuetify3.VCardText(classes="py-1 px-2"):
|
| 500 |
+
trame_html.Div("Load previously saved IBM/IonQ QPU job results (JSON format)",
|
| 501 |
+
classes="text-caption text-medium-emphasis mb-2")
|
| 502 |
+
|
| 503 |
+
# Platform and Field Type row
|
| 504 |
+
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 505 |
+
with vuetify3.VCol(cols=6):
|
| 506 |
+
vuetify3.VSelect(
|
| 507 |
+
label="Platform",
|
| 508 |
+
v_model=("em_job_platform", "IBM"),
|
| 509 |
+
items=("['IBM', 'IonQ']",),
|
| 510 |
+
density="compact",
|
| 511 |
+
hide_details=True,
|
| 512 |
+
color="primary",
|
| 513 |
+
)
|
| 514 |
+
with vuetify3.VCol(cols=6):
|
| 515 |
+
vuetify3.VSelect(
|
| 516 |
+
label="Field Type",
|
| 517 |
+
v_model=("em_job_field_type", "Ez"),
|
| 518 |
+
items=("['Ez', 'Hx', 'Hy']",),
|
| 519 |
+
density="compact",
|
| 520 |
+
hide_details=True,
|
| 521 |
+
color="primary",
|
| 522 |
+
)
|
| 523 |
+
|
| 524 |
+
# Monitor position row
|
| 525 |
+
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 526 |
+
with vuetify3.VCol(cols=12):
|
| 527 |
+
vuetify3.VTextField(
|
| 528 |
+
label="Monitor Point (x, y)",
|
| 529 |
+
v_model=("em_job_monitor_point", "(0, 0)"),
|
| 530 |
+
density="compact",
|
| 531 |
+
hide_details=True,
|
| 532 |
+
color="primary",
|
| 533 |
+
placeholder="(x, y)",
|
| 534 |
+
)
|
| 535 |
+
|
| 536 |
+
# Time and grid parameters row
|
| 537 |
+
with vuetify3.VRow(dense=True, classes="mb-2"):
|
| 538 |
+
with vuetify3.VCol(cols=4):
|
| 539 |
+
vuetify3.VTextField(
|
| 540 |
+
label="Total Time (T)",
|
| 541 |
+
v_model=("em_job_total_time", 1.0),
|
| 542 |
+
type="number",
|
| 543 |
+
density="compact",
|
| 544 |
+
hide_details=True,
|
| 545 |
+
color="primary",
|
| 546 |
+
)
|
| 547 |
+
with vuetify3.VCol(cols=4):
|
| 548 |
+
vuetify3.VTextField(
|
| 549 |
+
label="Snapshot Δt",
|
| 550 |
+
v_model=("em_job_snapshot_dt", 0.1),
|
| 551 |
+
type="number",
|
| 552 |
+
density="compact",
|
| 553 |
+
hide_details=True,
|
| 554 |
+
color="primary",
|
| 555 |
+
)
|
| 556 |
+
with vuetify3.VCol(cols=4):
|
| 557 |
+
vuetify3.VTextField(
|
| 558 |
+
label="No. of Points per Direction",
|
| 559 |
+
v_model=("em_job_nx", 4),
|
| 560 |
+
type="number",
|
| 561 |
+
density="compact",
|
| 562 |
+
hide_details=True,
|
| 563 |
+
color="primary",
|
| 564 |
+
)
|
| 565 |
+
|
| 566 |
+
# File upload and Generate button
|
| 567 |
+
with vuetify3.VRow(dense=True, classes="align-center mt-2"):
|
| 568 |
+
with vuetify3.VCol(cols=8):
|
| 569 |
+
vuetify3.VFileInput(
|
| 570 |
+
label="Upload Job Result (JSON)",
|
| 571 |
+
v_model=("em_job_upload", None),
|
| 572 |
+
accept=".json",
|
| 573 |
+
prepend_icon="mdi-file-upload",
|
| 574 |
+
density="compact",
|
| 575 |
+
hide_details=True,
|
| 576 |
+
color="primary",
|
| 577 |
+
show_size=True,
|
| 578 |
+
clearable=True,
|
| 579 |
+
)
|
| 580 |
+
with vuetify3.VCol(cols=4):
|
| 581 |
+
vuetify3.VBtn(
|
| 582 |
+
text="Generate",
|
| 583 |
+
color="secondary",
|
| 584 |
+
variant="tonal",
|
| 585 |
+
block=True,
|
| 586 |
+
disabled=("!em_job_upload || em_job_is_processing", True),
|
| 587 |
+
loading=("em_job_is_processing", False),
|
| 588 |
+
click=process_uploaded_em_job_result,
|
| 589 |
+
prepend_icon="mdi-chart-box-outline",
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
# Success message
|
| 593 |
+
vuetify3.VAlert(
|
| 594 |
+
v_if="em_job_upload_success",
|
| 595 |
+
type="success",
|
| 596 |
+
variant="tonal",
|
| 597 |
+
density="compact",
|
| 598 |
+
closable=True,
|
| 599 |
+
children=["{{ em_job_upload_success }}"],
|
| 600 |
+
classes="mt-2",
|
| 601 |
+
)
|
| 602 |
+
|
| 603 |
+
# Error message
|
| 604 |
+
vuetify3.VAlert(
|
| 605 |
+
v_if="em_job_upload_error",
|
| 606 |
+
type="error",
|
| 607 |
+
variant="tonal",
|
| 608 |
+
density="compact",
|
| 609 |
+
closable=True,
|
| 610 |
+
children=["{{ em_job_upload_error }}"],
|
| 611 |
+
classes="mt-2",
|
| 612 |
+
)
|
| 613 |
+
|
| 614 |
+
|
| 615 |
def _build_run_buttons():
|
| 616 |
"""Build the Run and Stop buttons row."""
|
| 617 |
with vuetify3.VRow(dense=True, classes="mb-2"):
|