gsim 0.0.0__py3-none-any.whl → 0.0.3__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 +1 -1
- gsim/common/__init__.py +57 -0
- gsim/common/geometry.py +76 -0
- gsim/{palace → common}/stack/__init__.py +8 -5
- gsim/{palace → common}/stack/extractor.py +33 -107
- gsim/{palace → common}/stack/materials.py +27 -11
- gsim/{palace → common}/stack/visualization.py +3 -3
- gsim/gcloud.py +78 -25
- gsim/palace/__init__.py +85 -45
- gsim/palace/base.py +373 -0
- gsim/palace/driven.py +728 -0
- gsim/palace/eigenmode.py +557 -0
- gsim/palace/electrostatic.py +398 -0
- gsim/palace/mesh/__init__.py +13 -1
- gsim/palace/mesh/config_generator.py +367 -0
- gsim/palace/mesh/generator.py +66 -744
- gsim/palace/mesh/geometry.py +472 -0
- gsim/palace/mesh/groups.py +170 -0
- gsim/palace/mesh/pipeline.py +22 -1
- gsim/palace/models/__init__.py +53 -0
- gsim/palace/models/geometry.py +34 -0
- gsim/palace/models/mesh.py +95 -0
- gsim/palace/models/numerical.py +66 -0
- gsim/palace/models/ports.py +137 -0
- gsim/palace/models/problems.py +195 -0
- gsim/palace/models/results.py +160 -0
- gsim/palace/models/stack.py +59 -0
- gsim/palace/ports/config.py +1 -1
- gsim/viz.py +9 -6
- {gsim-0.0.0.dist-info → gsim-0.0.3.dist-info}/METADATA +9 -6
- gsim-0.0.3.dist-info/RECORD +35 -0
- {gsim-0.0.0.dist-info → gsim-0.0.3.dist-info}/WHEEL +1 -1
- gsim-0.0.0.dist-info/RECORD +0 -18
- {gsim-0.0.0.dist-info → gsim-0.0.3.dist-info}/top_level.txt +0 -0
|
@@ -43,15 +43,15 @@ def _get_gds_layer_number(layer_level: LayerLevel) -> int | None:
|
|
|
43
43
|
if hasattr(layer, "layer"):
|
|
44
44
|
inner = layer.layer
|
|
45
45
|
if hasattr(inner, "layer"):
|
|
46
|
-
return int(inner.layer)
|
|
46
|
+
return int(inner.layer) # type: ignore[arg-type]
|
|
47
47
|
if isinstance(inner, int):
|
|
48
48
|
return int(inner)
|
|
49
49
|
|
|
50
50
|
# Handle enum with value
|
|
51
51
|
if hasattr(layer, "value"):
|
|
52
52
|
if isinstance(layer.value, tuple):
|
|
53
|
-
return int(layer.value[0])
|
|
54
|
-
return int(layer.value)
|
|
53
|
+
return int(layer.value[0]) # type: ignore[arg-type]
|
|
54
|
+
return int(layer.value) # type: ignore[arg-type]
|
|
55
55
|
|
|
56
56
|
return None
|
|
57
57
|
|
gsim/gcloud.py
CHANGED
|
@@ -16,17 +16,74 @@ Usage:
|
|
|
16
16
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
-
import zipfile
|
|
20
19
|
from pathlib import Path
|
|
21
20
|
from typing import TYPE_CHECKING
|
|
22
21
|
|
|
23
|
-
from gdsfactoryplus import sim
|
|
22
|
+
from gdsfactoryplus import sim
|
|
24
23
|
|
|
25
24
|
if TYPE_CHECKING:
|
|
26
25
|
from collections.abc import Callable
|
|
27
26
|
from typing import Literal
|
|
28
27
|
|
|
29
28
|
|
|
29
|
+
def _handle_failed_job(job, output_dir: Path, verbose: bool) -> None:
|
|
30
|
+
"""Handle a failed simulation job by downloading logs and raising informative error.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
job: The finished Job object with non-zero exit code
|
|
34
|
+
output_dir: Directory to download logs to
|
|
35
|
+
verbose: Whether to print progress
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
RuntimeError: Always raised with detailed error information
|
|
39
|
+
"""
|
|
40
|
+
error_parts = [
|
|
41
|
+
f"Simulation failed with exit code {job.exit_code}",
|
|
42
|
+
f"Status: {job.status.value}",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
if job.status_reason:
|
|
46
|
+
error_parts.append(f"Reason: {job.status_reason}")
|
|
47
|
+
if job.detail_reason:
|
|
48
|
+
error_parts.append(f"Details: {job.detail_reason}")
|
|
49
|
+
|
|
50
|
+
# Try to download logs even though job failed
|
|
51
|
+
try:
|
|
52
|
+
if job.download_urls:
|
|
53
|
+
if verbose:
|
|
54
|
+
print("Downloading logs from failed job...") # noqa: T201
|
|
55
|
+
|
|
56
|
+
raw_results = sim.download_results(job, output_dir=output_dir)
|
|
57
|
+
|
|
58
|
+
# Flatten results to find all files
|
|
59
|
+
all_files: dict[str, Path] = {}
|
|
60
|
+
for result_path in raw_results.values():
|
|
61
|
+
if result_path.is_dir():
|
|
62
|
+
for file_path in result_path.rglob("*"):
|
|
63
|
+
if file_path.is_file() and not file_path.name.startswith("."):
|
|
64
|
+
all_files[file_path.name] = file_path
|
|
65
|
+
else:
|
|
66
|
+
all_files[result_path.name] = result_path
|
|
67
|
+
|
|
68
|
+
# Look for log files and display them
|
|
69
|
+
log_files = ["palace.log", "stdout.log", "stderr.log", "output.log"]
|
|
70
|
+
for log_name in log_files:
|
|
71
|
+
if log_name in all_files:
|
|
72
|
+
content = all_files[log_name].read_text()
|
|
73
|
+
error_parts.append(f"\n--- {log_name} (last 100 lines) ---")
|
|
74
|
+
lines = content.strip().split("\n")
|
|
75
|
+
error_parts.append("\n".join(lines[-100:]))
|
|
76
|
+
break
|
|
77
|
+
|
|
78
|
+
if verbose and all_files:
|
|
79
|
+
print(f"Logs downloaded to {output_dir}") # noqa: T201
|
|
80
|
+
|
|
81
|
+
except Exception as e:
|
|
82
|
+
error_parts.append(f"(Failed to download logs: {e})")
|
|
83
|
+
|
|
84
|
+
raise RuntimeError("\n".join(error_parts))
|
|
85
|
+
|
|
86
|
+
|
|
30
87
|
def _get_job_definition(job_type: str):
|
|
31
88
|
"""Get JobDefinition enum value by name."""
|
|
32
89
|
job_type_upper = job_type.upper()
|
|
@@ -37,7 +94,7 @@ def _get_job_definition(job_type: str):
|
|
|
37
94
|
|
|
38
95
|
|
|
39
96
|
def upload_simulation_dir(input_dir: str | Path, job_type: str):
|
|
40
|
-
"""
|
|
97
|
+
"""Upload a simulation directory for cloud execution.
|
|
41
98
|
|
|
42
99
|
Args:
|
|
43
100
|
input_dir: Directory containing simulation files
|
|
@@ -47,21 +104,8 @@ def upload_simulation_dir(input_dir: str | Path, job_type: str):
|
|
|
47
104
|
PreJob object from gdsfactoryplus
|
|
48
105
|
"""
|
|
49
106
|
input_dir = Path(input_dir)
|
|
50
|
-
|
|
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
|
|
107
|
+
job_definition = _get_job_definition(job_type)
|
|
108
|
+
return sim.upload_simulation(path=input_dir, job_definition=job_definition)
|
|
65
109
|
|
|
66
110
|
|
|
67
111
|
def run_simulation(
|
|
@@ -125,18 +169,27 @@ def run_simulation(
|
|
|
125
169
|
|
|
126
170
|
# Check status
|
|
127
171
|
if finished_job.exit_code != 0:
|
|
128
|
-
|
|
129
|
-
f"Simulation failed with exit code {finished_job.exit_code}. "
|
|
130
|
-
f"Status: {finished_job.status.value}"
|
|
131
|
-
)
|
|
172
|
+
_handle_failed_job(finished_job, output_dir, verbose)
|
|
132
173
|
|
|
133
174
|
# Download
|
|
134
|
-
|
|
175
|
+
raw_results = sim.download_results(finished_job)
|
|
176
|
+
|
|
177
|
+
# Flatten results: gdsfactoryplus returns extracted directories,
|
|
178
|
+
# but we want a dict of filename -> Path for individual files
|
|
179
|
+
results: dict[str, Path] = {}
|
|
180
|
+
for result_path in raw_results.values():
|
|
181
|
+
if result_path.is_dir():
|
|
182
|
+
# Recursively find all files in the extracted directory
|
|
183
|
+
for file_path in result_path.rglob("*"):
|
|
184
|
+
if file_path.is_file() and not file_path.name.startswith("."):
|
|
185
|
+
results[file_path.name] = file_path
|
|
186
|
+
else:
|
|
187
|
+
results[result_path.name] = result_path
|
|
135
188
|
|
|
136
189
|
if verbose and results:
|
|
190
|
+
# Find common parent directory for display
|
|
137
191
|
first_path = next(iter(results.values()))
|
|
138
|
-
|
|
139
|
-
print(f"Downloaded {len(results)} files to {download_dir}") # noqa: T201
|
|
192
|
+
print(f"Downloaded {len(results)} files to {first_path.parent}") # noqa: T201
|
|
140
193
|
|
|
141
194
|
return results
|
|
142
195
|
|
gsim/palace/__init__.py
CHANGED
|
@@ -4,46 +4,61 @@ This module provides a comprehensive API for setting up and running
|
|
|
4
4
|
electromagnetic simulations using the Palace solver with gdsfactory components.
|
|
5
5
|
|
|
6
6
|
Features:
|
|
7
|
+
- Problem-specific simulation classes (DrivenSim, EigenmodeSim, ElectrostaticSim)
|
|
7
8
|
- Layer stack extraction from PDK
|
|
8
9
|
- Port configuration (inplane, via, CPW)
|
|
9
10
|
- Mesh generation with COMSOL-style presets
|
|
10
11
|
- Palace config file generation
|
|
11
12
|
|
|
12
13
|
Usage:
|
|
13
|
-
from gsim.palace import
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
)
|
|
14
|
+
from gsim.palace import DrivenSim
|
|
15
|
+
|
|
16
|
+
# Create and configure simulation
|
|
17
|
+
sim = DrivenSim()
|
|
18
|
+
sim.set_geometry(component)
|
|
19
|
+
sim.set_stack(air_above=300.0)
|
|
20
|
+
sim.add_cpw_port("P2", "P1", layer="topmetal2", length=5.0)
|
|
21
|
+
sim.set_driven(fmin=1e9, fmax=100e9)
|
|
22
|
+
|
|
23
|
+
# Generate mesh and run
|
|
24
|
+
sim.mesh("./sim", preset="fine")
|
|
25
|
+
results = sim.simulate()
|
|
39
26
|
"""
|
|
40
27
|
|
|
41
28
|
from __future__ import annotations
|
|
42
29
|
|
|
43
30
|
from functools import partial
|
|
44
31
|
|
|
32
|
+
# Common components (shared with FDTD)
|
|
33
|
+
from gsim.common import Geometry, LayerStack, Stack
|
|
34
|
+
|
|
35
|
+
# Stack utilities (from common, shared with FDTD)
|
|
36
|
+
from gsim.common.stack import (
|
|
37
|
+
MATERIALS_DB,
|
|
38
|
+
Layer,
|
|
39
|
+
MaterialProperties,
|
|
40
|
+
StackLayer,
|
|
41
|
+
extract_from_pdk,
|
|
42
|
+
extract_layer_stack,
|
|
43
|
+
get_material_properties,
|
|
44
|
+
get_stack,
|
|
45
|
+
load_stack_yaml,
|
|
46
|
+
material_is_conductor,
|
|
47
|
+
material_is_dielectric,
|
|
48
|
+
parse_layer_stack,
|
|
49
|
+
plot_stack,
|
|
50
|
+
print_stack,
|
|
51
|
+
print_stack_table,
|
|
52
|
+
)
|
|
45
53
|
from gsim.gcloud import print_job_summary
|
|
46
54
|
from gsim.gcloud import run_simulation as _run_simulation
|
|
55
|
+
|
|
56
|
+
# New simulation classes (composition, no inheritance)
|
|
57
|
+
from gsim.palace.driven import DrivenSim
|
|
58
|
+
from gsim.palace.eigenmode import EigenmodeSim
|
|
59
|
+
from gsim.palace.electrostatic import ElectrostaticSim
|
|
60
|
+
|
|
61
|
+
# Mesh utilities
|
|
47
62
|
from gsim.palace.mesh import (
|
|
48
63
|
GroundPlane,
|
|
49
64
|
MeshConfig,
|
|
@@ -51,6 +66,29 @@ from gsim.palace.mesh import (
|
|
|
51
66
|
MeshResult,
|
|
52
67
|
generate_mesh,
|
|
53
68
|
)
|
|
69
|
+
|
|
70
|
+
# Models (new submodule)
|
|
71
|
+
from gsim.palace.models import (
|
|
72
|
+
CPWPortConfig,
|
|
73
|
+
DrivenConfig,
|
|
74
|
+
EigenmodeConfig,
|
|
75
|
+
ElectrostaticConfig,
|
|
76
|
+
GeometryConfig,
|
|
77
|
+
MagnetostaticConfig,
|
|
78
|
+
MaterialConfig,
|
|
79
|
+
NumericalConfig,
|
|
80
|
+
PortConfig,
|
|
81
|
+
SimulationResult,
|
|
82
|
+
TerminalConfig,
|
|
83
|
+
TransientConfig,
|
|
84
|
+
ValidationResult,
|
|
85
|
+
WavePortConfig,
|
|
86
|
+
)
|
|
87
|
+
from gsim.palace.models import (
|
|
88
|
+
MeshConfig as MeshConfigModel,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Port utilities
|
|
54
92
|
from gsim.palace.ports import (
|
|
55
93
|
PalacePort,
|
|
56
94
|
PortGeometry,
|
|
@@ -60,41 +98,43 @@ from gsim.palace.ports import (
|
|
|
60
98
|
configure_via_port,
|
|
61
99
|
extract_ports,
|
|
62
100
|
)
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
)
|
|
101
|
+
|
|
102
|
+
# Visualization
|
|
82
103
|
from gsim.viz import plot_mesh
|
|
83
104
|
|
|
84
105
|
__all__ = [
|
|
85
106
|
"MATERIALS_DB",
|
|
107
|
+
"CPWPortConfig",
|
|
108
|
+
"DrivenConfig",
|
|
109
|
+
"DrivenSim",
|
|
110
|
+
"EigenmodeConfig",
|
|
111
|
+
"EigenmodeSim",
|
|
112
|
+
"ElectrostaticConfig",
|
|
113
|
+
"ElectrostaticSim",
|
|
114
|
+
"Geometry",
|
|
115
|
+
"GeometryConfig",
|
|
86
116
|
"GroundPlane",
|
|
87
117
|
"Layer",
|
|
88
118
|
"LayerStack",
|
|
119
|
+
"MagnetostaticConfig",
|
|
120
|
+
"MaterialConfig",
|
|
89
121
|
"MaterialProperties",
|
|
90
122
|
"MeshConfig",
|
|
123
|
+
"MeshConfigModel",
|
|
91
124
|
"MeshPreset",
|
|
92
125
|
"MeshResult",
|
|
126
|
+
"NumericalConfig",
|
|
93
127
|
"PalacePort",
|
|
128
|
+
"PortConfig",
|
|
94
129
|
"PortGeometry",
|
|
95
130
|
"PortType",
|
|
131
|
+
"SimulationResult",
|
|
132
|
+
"Stack",
|
|
96
133
|
"StackLayer",
|
|
134
|
+
"TerminalConfig",
|
|
135
|
+
"TransientConfig",
|
|
97
136
|
"ValidationResult",
|
|
137
|
+
"WavePortConfig",
|
|
98
138
|
"configure_cpw_port",
|
|
99
139
|
"configure_inplane_port",
|
|
100
140
|
"configure_via_port",
|