gsim 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gsim/__init__.py +16 -0
- gsim/gcloud.py +169 -0
- gsim/palace/__init__.py +120 -0
- gsim/palace/mesh/__init__.py +50 -0
- gsim/palace/mesh/generator.py +956 -0
- gsim/palace/mesh/gmsh_utils.py +484 -0
- gsim/palace/mesh/pipeline.py +188 -0
- gsim/palace/ports/__init__.py +35 -0
- gsim/palace/ports/config.py +363 -0
- gsim/palace/stack/__init__.py +149 -0
- gsim/palace/stack/extractor.py +602 -0
- gsim/palace/stack/materials.py +161 -0
- gsim/palace/stack/visualization.py +630 -0
- gsim/viz.py +86 -0
- gsim-0.0.0.dist-info/METADATA +128 -0
- gsim-0.0.0.dist-info/RECORD +18 -0
- gsim-0.0.0.dist-info/WHEEL +5 -0
- gsim-0.0.0.dist-info/top_level.txt +1 -0
gsim/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""GSIM - GDSFactory+ Simulation Tools.
|
|
2
|
+
|
|
3
|
+
This package provides APIs and client SDKs for accessing simulation tools
|
|
4
|
+
of gdsfactory+.
|
|
5
|
+
|
|
6
|
+
Currently includes:
|
|
7
|
+
- palace: Palace EM simulation API
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
__version__ = "0.0.0"
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"__version__",
|
|
16
|
+
]
|
gsim/gcloud.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"""GDSFactory+ cloud simulation interface.
|
|
2
|
+
|
|
3
|
+
This module provides an interface to run simulations on
|
|
4
|
+
the GDSFactory+ cloud infrastructure.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from gsim import gcloud
|
|
8
|
+
|
|
9
|
+
# Run simulation (uploads, starts, waits, downloads)
|
|
10
|
+
results = gcloud.run_simulation("./sim", job_type="palace")
|
|
11
|
+
|
|
12
|
+
# Or use solver-specific wrappers:
|
|
13
|
+
from gsim import palace as pa
|
|
14
|
+
results = pa.run_simulation("./sim")
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import zipfile
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import TYPE_CHECKING
|
|
22
|
+
|
|
23
|
+
from gdsfactoryplus import sim # type: ignore[import-untyped]
|
|
24
|
+
|
|
25
|
+
if TYPE_CHECKING:
|
|
26
|
+
from collections.abc import Callable
|
|
27
|
+
from typing import Literal
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _get_job_definition(job_type: str):
|
|
31
|
+
"""Get JobDefinition enum value by name."""
|
|
32
|
+
job_type_upper = job_type.upper()
|
|
33
|
+
if not hasattr(sim.JobDefinition, job_type_upper):
|
|
34
|
+
valid = [e.name for e in sim.JobDefinition]
|
|
35
|
+
raise ValueError(f"Unknown job type '{job_type}'. Valid types: {valid}")
|
|
36
|
+
return getattr(sim.JobDefinition, job_type_upper)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def upload_simulation_dir(input_dir: str | Path, job_type: str):
|
|
40
|
+
"""Zip all files in a directory and upload for simulation.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
input_dir: Directory containing simulation files
|
|
44
|
+
job_type: Simulation type (e.g., "palace")
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
PreJob object from gdsfactoryplus
|
|
48
|
+
"""
|
|
49
|
+
input_dir = Path(input_dir)
|
|
50
|
+
zip_path = Path("_gsim_upload.zip")
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_STORED) as zf:
|
|
54
|
+
for file in input_dir.rglob("*"):
|
|
55
|
+
if file.is_file():
|
|
56
|
+
zf.write(file, arcname=file.relative_to(input_dir))
|
|
57
|
+
|
|
58
|
+
job_definition = _get_job_definition(job_type)
|
|
59
|
+
pre_job = sim.upload_simulation(path=zip_path, job_definition=job_definition)
|
|
60
|
+
finally:
|
|
61
|
+
if zip_path.exists():
|
|
62
|
+
zip_path.unlink()
|
|
63
|
+
|
|
64
|
+
return pre_job
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def run_simulation(
|
|
68
|
+
output_dir: str | Path,
|
|
69
|
+
job_type: Literal["palace"] = "palace",
|
|
70
|
+
verbose: bool = True,
|
|
71
|
+
on_started: Callable | None = None,
|
|
72
|
+
) -> dict[str, Path]:
|
|
73
|
+
"""Run a simulation on GDSFactory+ cloud.
|
|
74
|
+
|
|
75
|
+
This function handles the complete workflow:
|
|
76
|
+
1. Uploads simulation files
|
|
77
|
+
2. Starts the simulation job
|
|
78
|
+
3. Waits for completion
|
|
79
|
+
4. Downloads results
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
output_dir: Directory containing the simulation files
|
|
83
|
+
job_type: Type of simulation (default: "palace")
|
|
84
|
+
verbose: Print progress messages (default True)
|
|
85
|
+
on_started: Optional callback called with job object when simulation starts
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Dict mapping result filename to local Path.
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
RuntimeError: If simulation fails
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
>>> results = gcloud.run_simulation("./sim", job_type="palace")
|
|
95
|
+
Uploading simulation... done
|
|
96
|
+
Job started: palace-abc123
|
|
97
|
+
Waiting for completion... done (2m 34s)
|
|
98
|
+
Downloading results... done
|
|
99
|
+
"""
|
|
100
|
+
output_dir = Path(output_dir)
|
|
101
|
+
|
|
102
|
+
if not output_dir.exists():
|
|
103
|
+
raise FileNotFoundError(f"Output directory not found: {output_dir}")
|
|
104
|
+
|
|
105
|
+
# Upload
|
|
106
|
+
if verbose:
|
|
107
|
+
print("Uploading simulation... ", end="", flush=True) # noqa: T201
|
|
108
|
+
|
|
109
|
+
pre_job = upload_simulation_dir(output_dir, job_type)
|
|
110
|
+
|
|
111
|
+
if verbose:
|
|
112
|
+
print("done") # noqa: T201
|
|
113
|
+
|
|
114
|
+
# Start
|
|
115
|
+
job = sim.start_simulation(pre_job)
|
|
116
|
+
|
|
117
|
+
if verbose:
|
|
118
|
+
print(f"Job started: {job.job_name}") # noqa: T201
|
|
119
|
+
|
|
120
|
+
if on_started:
|
|
121
|
+
on_started(job)
|
|
122
|
+
|
|
123
|
+
# Wait
|
|
124
|
+
finished_job = sim.wait_for_simulation(job)
|
|
125
|
+
|
|
126
|
+
# Check status
|
|
127
|
+
if finished_job.exit_code != 0:
|
|
128
|
+
raise RuntimeError(
|
|
129
|
+
f"Simulation failed with exit code {finished_job.exit_code}. "
|
|
130
|
+
f"Status: {finished_job.status.value}"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# Download
|
|
134
|
+
results = sim.download_results(finished_job)
|
|
135
|
+
|
|
136
|
+
if verbose and results:
|
|
137
|
+
first_path = next(iter(results.values()))
|
|
138
|
+
download_dir = first_path.parent
|
|
139
|
+
print(f"Downloaded {len(results)} files to {download_dir}") # noqa: T201
|
|
140
|
+
|
|
141
|
+
return results
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def print_job_summary(job) -> None:
|
|
145
|
+
"""Print a formatted summary of a simulation job.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
job: Job object from gdsfactoryplus
|
|
149
|
+
"""
|
|
150
|
+
if job.started_at and job.finished_at:
|
|
151
|
+
delta = job.finished_at - job.started_at
|
|
152
|
+
minutes, seconds = divmod(int(delta.total_seconds()), 60)
|
|
153
|
+
duration = f"{minutes}m {seconds}s"
|
|
154
|
+
else:
|
|
155
|
+
duration = "N/A"
|
|
156
|
+
|
|
157
|
+
size_kb = job.output_size_bytes / 1024
|
|
158
|
+
size_str = f"{size_kb:.1f} KB" if size_kb < 1024 else f"{size_kb / 1024:.2f} MB"
|
|
159
|
+
files = list(job.download_urls.keys()) if job.download_urls else []
|
|
160
|
+
|
|
161
|
+
print(f"{'Job:':<12} {job.job_name}") # noqa: T201
|
|
162
|
+
print(f"{'Status:':<12} {job.status.value} (exit {job.exit_code})") # noqa: T201
|
|
163
|
+
print(f"{'Duration:':<12} {duration}") # noqa: T201
|
|
164
|
+
mem_gb = job.requested_memory_mb // 1024
|
|
165
|
+
print(f"{'Resources:':<12} {job.requested_cpu} CPU / {mem_gb} GB") # noqa: T201
|
|
166
|
+
print(f"{'Output:':<12} {size_str}") # noqa: T201
|
|
167
|
+
print(f"{'Files:':<12} {len(files)} files") # noqa: T201
|
|
168
|
+
for f in files:
|
|
169
|
+
print(f" - {f}") # noqa: T201
|
gsim/palace/__init__.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"""Palace API module for EM simulation with gdsfactory.
|
|
2
|
+
|
|
3
|
+
This module provides a comprehensive API for setting up and running
|
|
4
|
+
electromagnetic simulations using the Palace solver with gdsfactory components.
|
|
5
|
+
|
|
6
|
+
Features:
|
|
7
|
+
- Layer stack extraction from PDK
|
|
8
|
+
- Port configuration (inplane, via, CPW)
|
|
9
|
+
- Mesh generation with COMSOL-style presets
|
|
10
|
+
- Palace config file generation
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
from gsim.palace import (
|
|
14
|
+
get_stack, configure_inplane_port, configure_via_port,
|
|
15
|
+
extract_ports, generate_mesh
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# 1. Get layer stack from active PDK
|
|
19
|
+
stack = get_stack()
|
|
20
|
+
|
|
21
|
+
# 2. Configure ports on component
|
|
22
|
+
# Inplane port (horizontal, on single layer - for CPW gaps)
|
|
23
|
+
configure_inplane_port(c.ports['o1'], layer='topmetal2', length=5.0)
|
|
24
|
+
configure_inplane_port(c.ports['o2'], layer='topmetal2', length=5.0)
|
|
25
|
+
|
|
26
|
+
# Via port (vertical, between two layers - for microstrip feed)
|
|
27
|
+
configure_via_port(c.ports['feed'], from_layer='metal1', to_layer='topmetal2')
|
|
28
|
+
|
|
29
|
+
# 3. Extract configured ports
|
|
30
|
+
ports = extract_ports(c, stack)
|
|
31
|
+
|
|
32
|
+
# 4. Generate mesh
|
|
33
|
+
result = generate_mesh(
|
|
34
|
+
component=c,
|
|
35
|
+
stack=stack,
|
|
36
|
+
ports=ports,
|
|
37
|
+
output_dir="./simulation",
|
|
38
|
+
)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
from __future__ import annotations
|
|
42
|
+
|
|
43
|
+
from functools import partial
|
|
44
|
+
|
|
45
|
+
from gsim.gcloud import print_job_summary
|
|
46
|
+
from gsim.gcloud import run_simulation as _run_simulation
|
|
47
|
+
from gsim.palace.mesh import (
|
|
48
|
+
GroundPlane,
|
|
49
|
+
MeshConfig,
|
|
50
|
+
MeshPreset,
|
|
51
|
+
MeshResult,
|
|
52
|
+
generate_mesh,
|
|
53
|
+
)
|
|
54
|
+
from gsim.palace.ports import (
|
|
55
|
+
PalacePort,
|
|
56
|
+
PortGeometry,
|
|
57
|
+
PortType,
|
|
58
|
+
configure_cpw_port,
|
|
59
|
+
configure_inplane_port,
|
|
60
|
+
configure_via_port,
|
|
61
|
+
extract_ports,
|
|
62
|
+
)
|
|
63
|
+
from gsim.palace.stack import (
|
|
64
|
+
MATERIALS_DB,
|
|
65
|
+
Layer,
|
|
66
|
+
LayerStack,
|
|
67
|
+
MaterialProperties,
|
|
68
|
+
StackLayer,
|
|
69
|
+
ValidationResult,
|
|
70
|
+
extract_from_pdk,
|
|
71
|
+
extract_layer_stack,
|
|
72
|
+
get_material_properties,
|
|
73
|
+
get_stack,
|
|
74
|
+
load_stack_yaml,
|
|
75
|
+
material_is_conductor,
|
|
76
|
+
material_is_dielectric,
|
|
77
|
+
parse_layer_stack,
|
|
78
|
+
plot_stack,
|
|
79
|
+
print_stack,
|
|
80
|
+
print_stack_table,
|
|
81
|
+
)
|
|
82
|
+
from gsim.viz import plot_mesh
|
|
83
|
+
|
|
84
|
+
__all__ = [
|
|
85
|
+
"MATERIALS_DB",
|
|
86
|
+
"GroundPlane",
|
|
87
|
+
"Layer",
|
|
88
|
+
"LayerStack",
|
|
89
|
+
"MaterialProperties",
|
|
90
|
+
"MeshConfig",
|
|
91
|
+
"MeshPreset",
|
|
92
|
+
"MeshResult",
|
|
93
|
+
"PalacePort",
|
|
94
|
+
"PortGeometry",
|
|
95
|
+
"PortType",
|
|
96
|
+
"StackLayer",
|
|
97
|
+
"ValidationResult",
|
|
98
|
+
"configure_cpw_port",
|
|
99
|
+
"configure_inplane_port",
|
|
100
|
+
"configure_via_port",
|
|
101
|
+
"extract_from_pdk",
|
|
102
|
+
"extract_layer_stack",
|
|
103
|
+
"extract_ports",
|
|
104
|
+
"generate_mesh",
|
|
105
|
+
"get_material_properties",
|
|
106
|
+
"get_stack",
|
|
107
|
+
"load_stack_yaml",
|
|
108
|
+
"material_is_conductor",
|
|
109
|
+
"material_is_dielectric",
|
|
110
|
+
"parse_layer_stack",
|
|
111
|
+
"plot_mesh",
|
|
112
|
+
"plot_stack",
|
|
113
|
+
"print_job_summary",
|
|
114
|
+
"print_stack",
|
|
115
|
+
"print_stack_table",
|
|
116
|
+
"run_simulation",
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
# Palace-specific run_simulation with job_type preset
|
|
120
|
+
run_simulation = partial(_run_simulation, job_type="palace")
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""3D mesh generation for Palace EM simulation.
|
|
2
|
+
|
|
3
|
+
This module provides mesh generation directly from gdsfactory components
|
|
4
|
+
and palace-api data structures.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
from gsim.palace.mesh import generate_mesh, MeshConfig
|
|
8
|
+
|
|
9
|
+
# Quick presets (based on COMSOL guidelines)
|
|
10
|
+
config = MeshConfig.coarse() # Fast iteration
|
|
11
|
+
config = MeshConfig.default() # Balanced (COMSOL default)
|
|
12
|
+
config = MeshConfig.fine() # High accuracy
|
|
13
|
+
|
|
14
|
+
# Or customize with overrides
|
|
15
|
+
config = MeshConfig.coarse(margin=100.0, fmax=50e9)
|
|
16
|
+
|
|
17
|
+
# Or full manual control
|
|
18
|
+
config = MeshConfig(refined_mesh_size=3.0, max_mesh_size=200.0)
|
|
19
|
+
|
|
20
|
+
result = generate_mesh(
|
|
21
|
+
component=c,
|
|
22
|
+
stack=stack,
|
|
23
|
+
ports=ports,
|
|
24
|
+
output_dir="./sim_output",
|
|
25
|
+
config=config,
|
|
26
|
+
)
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from gsim.palace.mesh.generator import generate_mesh as generate_mesh_direct
|
|
32
|
+
from gsim.palace.mesh.pipeline import (
|
|
33
|
+
GroundPlane,
|
|
34
|
+
MeshConfig,
|
|
35
|
+
MeshPreset,
|
|
36
|
+
MeshResult,
|
|
37
|
+
generate_mesh,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
from . import gmsh_utils
|
|
41
|
+
|
|
42
|
+
__all__ = [
|
|
43
|
+
"GroundPlane",
|
|
44
|
+
"MeshConfig",
|
|
45
|
+
"MeshPreset",
|
|
46
|
+
"MeshResult",
|
|
47
|
+
"generate_mesh",
|
|
48
|
+
"generate_mesh_direct",
|
|
49
|
+
"gmsh_utils",
|
|
50
|
+
]
|