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.
@@ -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 # type: ignore[import-untyped]
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
- """Zip all files in a directory and upload for simulation.
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
- 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
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
- raise RuntimeError(
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
- results = sim.download_results(finished_job)
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
- download_dir = first_path.parent
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
- 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
- )
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
- 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
- )
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",