9
9
import xarray as xr
10
10
11
11
from ....constants import C_0 , fp_eps
12
+ from ....exceptions import SetupError
12
13
from ....components .simulation import Simulation
13
14
from ....components .geometry .utils_2d import snap_coordinate_to_grid
14
15
from ....components .data .sim_data import SimulationData
@@ -148,7 +149,7 @@ def sim_dict(self) -> Dict[str, Simulation]:
148
149
list (self .simulation .monitors ) + port_voltage_monitors + port_current_monitors
149
150
)
150
151
for port , center in zip (self .ports , snap_centers ):
151
- port_source = self .to_source (self ._source_time , port = port , snap_center = center )
152
+ port_source = port .to_source (self ._source_time , snap_center = center )
152
153
update_dict = dict (
153
154
sources = [port_source ],
154
155
monitors = new_mnts ,
@@ -160,6 +161,9 @@ def sim_dict(self) -> Dict[str, Simulation]:
160
161
task_name = self ._task_name (port = port )
161
162
sim_dict [task_name ] = sim_copy
162
163
164
+ # Check final simulations for grid size at ports
165
+ for _ , sim in sim_dict .items ():
166
+ TerminalComponentModeler ._check_grid_size_at_ports (sim , self .ports )
163
167
return sim_dict
164
168
165
169
@cached_property
@@ -189,6 +193,15 @@ def _construct_smatrix(self, batch_data: BatchData) -> LumpedPortDataArray:
189
193
a_matrix = LumpedPortDataArray (values , coords = coords )
190
194
b_matrix = a_matrix .copy (deep = True )
191
195
196
+ def select_within_bounds (coords : np .array , min , max ):
197
+ """Helper to slice coordinates within min and max bounds, including a tolerance."""
198
+ """xarray does not have this functionality yet. """
199
+ np_idx = np .logical_and (coords > min , coords < max )
200
+ np_idx = np .logical_or (np_idx , np .isclose (coords , min , rtol = fp_eps , atol = fp_eps ))
201
+ np_idx = np .logical_or (np_idx , np .isclose (coords , max , rtol = fp_eps , atol = fp_eps ))
202
+ coords = coords [np_idx ]
203
+ return coords
204
+
192
205
def port_voltage (port : LumpedPort , sim_data : SimulationData ) -> xr .DataArray :
193
206
"""Helper to compute voltage across the port."""
194
207
e_component = "xyz" [port .voltage_axis ]
@@ -263,13 +276,21 @@ def port_current(port: LumpedPort, sim_data: SimulationData) -> xr.DataArray:
263
276
h1_field = h_field .isel ({orth_component : orth_index - 1 })
264
277
h2_field = h_field .isel ({orth_component : orth_index })
265
278
h_field = h1_field - h2_field
279
+
280
+ # Find exact bounds of port taking into consideration the Yee grid
281
+ np_coords = h_coords [port .current_axis ].values
282
+ port_min = port .bounds [0 ][port .current_axis ]
283
+ port_max = port .bounds [1 ][port .current_axis ]
284
+ np_coords = select_within_bounds (np_coords , port_min , port_max )
285
+ coord_low = np_coords [0 ]
286
+ coord_high = np_coords [- 1 ]
266
287
# Extract cap field which is coincident with sheet
267
288
h_cap = h_cap_field .isel ({orth_component : orth_index })
268
289
269
290
# Need to make sure to use the nearest coordinate that is
270
291
# at least greater than the port bounds
271
- hcap_minus = h_cap .sel ({h_component : slice (- np .inf , port . bounds [ 0 ][ port . current_axis ] )})
272
- hcap_plus = h_cap .sel ({h_component : slice (port . bounds [ 1 ][ port . current_axis ] , np .inf )})
292
+ hcap_minus = h_cap .sel ({h_component : slice (- np .inf , coord_low )})
293
+ hcap_plus = h_cap .sel ({h_component : slice (coord_high , np .inf )})
273
294
hcap_minus = hcap_minus .isel ({h_component : - 1 })
274
295
hcap_plus = hcap_plus .isel ({h_component : 0 })
275
296
# Length of integration along the h_cap contour is a single cell width
@@ -344,3 +365,21 @@ def _validate_3d_simulation(cls, val):
344
365
f"'{ cls .__name__ } ' must be setup with a 3D simulation with all sizes greater than 0."
345
366
)
346
367
return val
368
+
369
+ @staticmethod
370
+ def _check_grid_size_at_ports (simulation : Simulation , ports : list [LumpedPort ]):
371
+ """Raises :class:`SetupError` if the grid is too coarse at port locations"""
372
+ yee_grid = simulation .grid .yee
373
+ for port in ports :
374
+ e_component = "xyz" [port .voltage_axis ]
375
+ e_yee_grid = yee_grid .grid_dict [f"E{ e_component } " ]
376
+ coords = e_yee_grid .to_dict [e_component ]
377
+ min_bound = port .bounds [0 ][port .voltage_axis ]
378
+ max_bound = port .bounds [1 ][port .voltage_axis ]
379
+ coords_within_port = np .any (np .logical_and (coords > min_bound , coords < max_bound ))
380
+ if not coords_within_port :
381
+ raise SetupError (
382
+ f"Grid is too coarse along '{ e_component } ' direction for the lumped port "
383
+ f"at location { port .center } . Either set the port's 'num_grid_cells' to "
384
+ f"a nonzero integer or modify the 'GridSpec'. "
385
+ )
0 commit comments