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 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
@@ -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
+ ]