Skip to content

Base Classes

Core Classes

fdtdx.objects.object.SimulationObject

Bases: ExtendedTreeClass, ABC

Source code in src/fdtdx/objects/object.py
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
@extended_autoinit
class SimulationObject(ExtendedTreeClass, ABC):
    partial_real_shape: PartialRealShape3D = frozen_field(default=UNDEFINED_SHAPE_3D)
    partial_grid_shape: PartialGridShape3D = frozen_field(default=UNDEFINED_SHAPE_3D)
    placement_order: int = frozen_field(default=0)
    color: tuple[float, float, float] | None = frozen_field(default=None)  # RGB, interval[0, 1]
    name: str = frozen_field(  # type: ignore
        default=None,
        on_setattr=[UniqueName()],
    )
    max_random_real_offsets: tuple[float, float, float] = frozen_field(default=(0, 0, 0))
    max_random_grid_offsets: tuple[int, int, int] = frozen_field(default=(0, 0, 0))

    _grid_slice_tuple: SliceTuple3D = frozen_private_field(
        default=INVALID_SLICE_TUPLE_3D,
    )
    _config: SimulationConfig = frozen_private_field(default=DUMMY_SIMULATION_CONFIG)

    @property
    def grid_slice_tuple(self) -> SliceTuple3D:
        if self._grid_slice_tuple == INVALID_SLICE_TUPLE_3D:
            raise Exception(f"Object is not yet initialized: {self}")
        return self._grid_slice_tuple

    @property
    def grid_slice(self) -> Slice3D:
        tpl = ensure_slice_tuple(self._grid_slice_tuple)
        if len(tpl) != 3:
            raise Exception(f"Invalid slice tuple, this should never happen: {tpl}")
        return tpl[0], tpl[1], tpl[2]

    @property
    def real_shape(self) -> RealShape3D:
        grid_shape = self.grid_shape
        real_shape = (
            grid_shape[0] * self._config.resolution,
            grid_shape[1] * self._config.resolution,
            grid_shape[2] * self._config.resolution,
        )
        return real_shape

    @property
    def grid_shape(self) -> GridShape3D:
        if self._grid_slice_tuple == INVALID_SLICE_TUPLE_3D:
            raise Exception("Cannot compute shape on non-initialized object")
        return (
            self._grid_slice_tuple[0][1] - self._grid_slice_tuple[0][0],
            self._grid_slice_tuple[1][1] - self._grid_slice_tuple[1][0],
            self._grid_slice_tuple[2][1] - self._grid_slice_tuple[2][0],
        )

    def place_on_grid(
        self: Self,
        grid_slice_tuple: SliceTuple3D,
        config: SimulationConfig,
        key: jax.Array,
    ) -> Self:
        del key
        if self._grid_slice_tuple != INVALID_SLICE_TUPLE_3D:
            raise Exception(f"Object is already compiled to grid: {self}")
        for axis in range(3):
            s1, s2 = grid_slice_tuple[axis]
            if s1 < 0 or s2 < 0 or s2 <= s1:
                raise Exception(f"Invalid placement of object {self} at {grid_slice_tuple}")
        self = self.aset("_grid_slice_tuple", grid_slice_tuple)
        self = self.aset("_config", config)

        return self

    @abc.abstractmethod
    def get_inv_permittivity(
        self,
        prev_inv_permittivity: jax.Array,
        params: dict[str, jax.Array] | None,
    ) -> tuple[jax.Array, dict]:  # permittivity and info dict
        raise NotImplementedError()

    @abc.abstractmethod
    def get_inv_permeability(
        self,
        prev_inv_permeability: jax.Array,
        params: dict[str, jax.Array] | None,
    ) -> tuple[jax.Array, dict]:  # permeability and info dict
        raise NotImplementedError()

    def place_relative_to(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int,
        own_positions: tuple[float, ...] | float,
        other_positions: tuple[float, ...] | float,
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionalConstraint between two objects. The constraint is defined by anchor points on
        both objects, which are constrainted to be at the same position. Anchors are defined in relative coordinates,
        i.e. a position of -1 is the left object boundary in the repective axis and a position of +1 the right boundary.

        Args:
            other: Another object in the simulation scene
            axes: Eiter a single integer or a tuple describing the axes of the constraints
            own_positions: The positions of the own anchor in the axes. Must have the same lengths as axes
            other_positions: The positions of the other objects' anchor in the axes. Must have the same lengths as axes
            margins: The margins between the anchors of both objects in meters. Must have the same lengths as axes. Defaults to no margin
            grid_margins: The margins between the anchors of both objects in Yee-grid voxels. Must have the same lengths as axes. Defaults to no margin

        Returns:
            PositionConstraint: Positional constraint between this object and the other
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        if isinstance(own_positions, int | float):
            own_positions = tuple(
                [
                    float(own_positions),
                ]
            )
        if isinstance(other_positions, int | float):
            other_positions = tuple(
                [
                    float(other_positions),
                ]
            )
        if isinstance(margins, int | float):
            margins = tuple(
                [
                    float(margins),
                ]
            )
        if isinstance(grid_margins, int):
            grid_margins = tuple(
                [
                    grid_margins,
                ]
            )
        if margins is None:
            margins = tuple([0 for _ in axes])
        if grid_margins is None:
            grid_margins = tuple([0 for _ in axes])
        if (
            len(axes) != len(own_positions)
            or len(axes) != len(other_positions)
            or len(axes) != len(margins)
            or len(axes) != len(grid_margins)
        ):
            raise Exception("All inputs should have same lengths")
        constraint = PositionConstraint(
            axes=axes,
            other_object=other,
            object=self,
            other_object_positions=other_positions,
            object_positions=own_positions,
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def size_relative_to(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int,
        other_axes: tuple[int, ...] | int | None = None,
        proportions: tuple[float, ...] | float | None = None,
        offsets: tuple[float, ...] | float | None = None,
        grid_offsets: tuple[int, ...] | int | None = None,
    ) -> SizeConstraint:
        """Creates a SizeConstraint between two objects. The constraint defines the size of this object relative
        to another object, allowing for proportional scaling and offsets in specified axes.

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes of this object to constrain
            other_axes: Either a single integer or a tuple describing which axes of the other object to reference.
                If None, uses the same axes as specified in 'axes'
            proportions: Scale factors to apply to the other object's dimensions. Must have same length as axes.
                If None, defaults to 1.0 (same size)
            offsets: Additional size offsets in meters to apply after scaling. Must have same length as axes.
                If None, defaults to 0
            grid_offsets: Additional size offsets in Yee-grid voxels to apply after scaling. Must have same length as axes.
                If None, defaults to 0

        Returns:
            SizeConstraint: Size constraint between this object and the other
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        if isinstance(other_axes, int):
            other_axes = tuple(
                [
                    other_axes,
                ]
            )
        if isinstance(proportions, int | float):
            proportions = tuple(
                [
                    float(proportions),
                ]
            )
        if isinstance(offsets, int | float):
            offsets = tuple(
                [
                    float(offsets),
                ]
            )
        if isinstance(grid_offsets, int):
            grid_offsets = tuple(
                [
                    grid_offsets,
                ]
            )
        if offsets is None:
            offsets = tuple([0 for _ in axes])
        if grid_offsets is None:
            grid_offsets = tuple([0 for _ in axes])
        if proportions is None:
            proportions = tuple([1.0 for _ in axes])
        if other_axes is None:
            other_axes = tuple([a for a in axes])
        if len(axes) != len(proportions) or len(axes) != len(offsets) or len(axes) != len(grid_offsets):
            raise Exception("All inputs should have same lengths")
        constraint = SizeConstraint(
            other_object=other,
            object=self,
            axes=axes,
            other_axes=other_axes,
            proportions=proportions,
            offsets=offsets,
            grid_offsets=grid_offsets,
        )
        return constraint

    def same_size(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int = (0, 1, 2),
        offsets: tuple[float, ...] | float | None = None,
        grid_offsets: tuple[int, ...] | int | None = None,
    ) -> SizeConstraint:
        """Creates a SizeConstraint that makes this object the same size as another object along specified axes.
        This is a convenience wrapper around size_relative_to() with proportions set to 1.0.

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes should have the same size.
                Defaults to all axes (0, 1, 2)
            offsets: Additional size offsets in meters to apply. Must have same length as axes.
                If None, defaults to 0
            grid_offsets: Additional size offsets in Yee-grid voxels to apply. Must have same length as axes.
                If None, defaults to 0

        Returns:
            SizeConstraint: Size constraint ensuring equal sizes between objects
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        proportions = tuple([1 for _ in axes])
        constraint = self.size_relative_to(
            other=other,
            axes=axes,
            proportions=proportions,
            offsets=offsets,
            grid_offsets=grid_offsets,
        )
        return constraint

    def place_at_center(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int = (0, 1, 2),
        own_positions: tuple[float, ...] | float | None = None,
        other_positions: tuple[float, ...] | float | None = None,
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionConstraint that centers this object relative to another object along specified axes.
        This is a convenience wrapper around place_relative_to() with default positions at the center (0).

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes to center on.
                Defaults to all axes (0, 1, 2)
            own_positions: Relative positions on this object (-1 to 1). If None, defaults to center (0)
            other_positions: Relative positions on other object (-1 to 1). If None, defaults to center (0)
            margins: Additional margins in meters between objects. Must have same length as axes.
                If None, defaults to 0
            grid_margins: Additional margins in Yee-grid voxels between objects. Must have same length as axes.
                If None, defaults to 0

        Returns:
            PositionConstraint: Position constraint centering objects relative to each other
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        if isinstance(own_positions, int | float):
            own_positions = tuple(
                [
                    float(own_positions),
                ]
            )
        if isinstance(other_positions, int | float):
            other_positions = tuple(
                [
                    float(other_positions),
                ]
            )
        if own_positions is None:
            own_positions = tuple([0 for _ in axes])
        if other_positions is None:
            other_positions = tuple([0 for _ in axes])
        constraint = self.place_relative_to(
            other=other,
            axes=axes,
            own_positions=own_positions,
            other_positions=other_positions,
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def same_position_and_size(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int = (0, 1, 2),
    ) -> tuple[PositionConstraint, SizeConstraint]:
        """Creates both position and size constraints to make this object match another object's position and size.
        This is a convenience wrapper combining place_at_center() and same_size().

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes to match.
                Defaults to all axes (0, 1, 2)

        Returns:
            tuple[PositionConstraint, SizeConstraint]: Position and size constraints for matching objects
        """
        size_constraint = self.same_size(
            other=other,
            axes=axes,
        )
        pos_constraint = self.place_at_center(
            other=other,
            axes=axes,
        )
        return pos_constraint, size_constraint

    def face_to_face_positive_direction(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int,
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionConstraint that places this object facing another object in the positive direction
        of specified axes. The objects will touch at their facing boundaries unless margins are specified.

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes to align on
            margins: Additional margins in meters between the facing surfaces. Must have same length as axes.
                If None, defaults to 0
            grid_margins: Additional margins in Yee-grid voxels between the facing surfaces.
                Must have same length as axes. If None, defaults to 0

        Returns:
            PositionConstraint: Position constraint aligning objects face-to-face in positive direction
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        own_positions = tuple([-1 for _ in axes])
        other_positions = tuple([1 for _ in axes])
        constraint = self.place_relative_to(
            other=other,
            axes=axes,
            own_positions=own_positions,
            other_positions=other_positions,
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def face_to_face_negative_direction(
        self,
        other: "SimulationObject",
        axes: tuple[int, ...] | int,
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionConstraint that places this object facing another object in the negative direction
        of specified axes. The objects will touch at their facing boundaries unless margins are specified.

        Args:
            other: Another object in the simulation scene
            axes: Either a single integer or a tuple describing which axes to align on
            margins: Additional margins in meters between the facing surfaces. Must have same length as axes.
                If None, defaults to 0
            grid_margins: Additional margins in Yee-grid voxels between the facing surfaces.
                Must have same length as axes. If None, defaults to 0

        Returns:
            PositionConstraint: Position constraint aligning objects face-to-face in negative direction
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        own_positions = tuple([1 for _ in axes])
        other_positions = tuple([-1 for _ in axes])
        constraint = self.place_relative_to(
            other=other,
            axes=axes,
            own_positions=own_positions,
            other_positions=other_positions,
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def place_above(
        self,
        other: "SimulationObject",
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionConstraint that places this object above another object along the z-axis.
        This is a convenience wrapper around face_to_face_positive_direction() for axis 2 (z-axis).

        Args:
            other: Another object in the simulation scene
            margins: Additional vertical margins in meters between objects. If None, defaults to 0
            grid_margins: Additional vertical margins in Yee-grid voxels between objects.
                If None, defaults to 0

        Returns:
            PositionConstraint: Position constraint placing this object above the other
        """
        constraint = self.face_to_face_positive_direction(
            other=other,
            axes=(2,),
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def place_below(
        self,
        other: "SimulationObject",
        margins: tuple[float, ...] | float | None = None,
        grid_margins: tuple[int, ...] | int | None = None,
    ) -> PositionConstraint:
        """Creates a PositionConstraint that places this object below another object along the z-axis.
        This is a convenience wrapper around face_to_face_negative_direction() for axis 2 (z-axis).

        Args:
            other: Another object in the simulation scene
            margins: Additional vertical margins in meters between objects. If None, defaults to 0
            grid_margins: Additional vertical margins in Yee-grid voxels between objects.
                If None, defaults to 0

        Returns:
            PositionConstraint: Position constraint placing this object below the other
        """
        constraint = self.face_to_face_negative_direction(
            other=other,
            axes=(2,),
            margins=margins,
            grid_margins=grid_margins,
        )
        return constraint

    def set_grid_coordinates(
        self,
        axes: tuple[int, ...] | int,
        sides: tuple[Literal["+", "-"], ...] | Literal["+", "-"],
        coordinates: tuple[int, ...] | int,
    ) -> GridCoordinateConstraint:
        """Creates a GridCoordinateConstraint that forces specific sides of this object to align with
        given grid coordinates. Used for precise positioning in the discretized simulation space.

        Args:
            axes: Either a single integer or a tuple describing which axes to constrain
            sides: Either a single string or a tuple of strings ('+' or '-') indicating which side
                of each axis to constrain. Must have same length as axes
            coordinates: Either a single integer or a tuple of integers specifying the grid coordinates
                to align with. Must have same length as axes

        Returns:
            GridCoordinateConstraint: Constraint forcing alignment with specific grid coordinates
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        if isinstance(sides, str):
            sides = (sides,)
        if isinstance(coordinates, int):
            coordinates = tuple(
                [
                    coordinates,
                ]
            )
        if len(axes) != len(sides) or len(axes) != len(coordinates):
            raise Exception("All inputs need to have the same lengths!")
        return GridCoordinateConstraint(
            object=self,
            axes=axes,
            sides=sides,
            coordinates=coordinates,
        )

    def set_real_coordinates(
        self,
        axes: tuple[int, ...] | int,
        sides: tuple[Literal["+", "-"], ...] | Literal["+", "-"],
        coordinates: tuple[float, ...] | float,
    ) -> RealCoordinateConstraint:
        """Creates a RealCoordinateConstraint that forces specific sides of this object to align with
        given real-space coordinates. Used for precise positioning in physical units.

        Args:
            axes: Either a single integer or a tuple describing which axes to constrain
            sides: Either a single string or a tuple of strings ('+' or '-') indicating which side
                of each axis to constrain. Must have same length as axes
            coordinates: Either a single float or a tuple of floats specifying the real-space coordinates
                in meters to align with. Must have same length as axes

        Returns:
            RealCoordinateConstraint: Constraint forcing alignment with specific real-space coordinates
        """
        if isinstance(axes, int):
            axes = tuple(
                [
                    axes,
                ]
            )
        if isinstance(sides, str):
            sides = (sides,)
        if isinstance(coordinates, int | float):
            coordinates = tuple(
                [
                    float(coordinates),
                ]
            )
        if len(axes) != len(sides) or len(axes) != len(coordinates):
            raise Exception("All inputs need to have the same lengths!")
        return RealCoordinateConstraint(
            object=self,
            axes=axes,
            sides=sides,
            coordinates=coordinates,
        )

    def extend_to(
        self,
        other: Optional["SimulationObject"],
        axis: int,
        direction: Literal["+", "-"],
        other_position: float | None = None,
        offset: float = 0,
        grid_offset: int = 0,
    ) -> SizeExtensionConstraint:
        """Creates a SizeExtensionConstraint that extends this object along a specified axis until it
        reaches another object or the simulation boundary. The extension can be in either positive or
        negative direction.

        Args:
            other: Target object to extend to, or None to extend to simulation boundary
            axis: Which axis to extend along (0, 1, or 2)
            direction: Direction to extend in ('+' or '-')
            other_position: Relative position on target object (-1 to 1) to extend to.
                If None, defaults to the corresponding side (-1 for '+' direction, 1 for '-' direction)
            offset: Additional offset in meters to apply after extension. Ignored when extending to
                simulation boundary
            grid_offset: Additional offset in Yee-grid voxels to apply after extension. Ignored when
                extending to simulation boundary

        Returns:
            SizeExtensionConstraint: Constraint defining how the object extends
        """
        # default: extend to corresponding side
        if other_position is None:
            other_position = -1 if direction == "+" else 1
        if other is None:
            if offset != 0 or grid_offset != 0:
                raise Exception("Cannot use offset when extending object to infinity")
        return SizeExtensionConstraint(
            object=self,
            other_object=other,
            axis=axis,
            direction=direction,
            other_position=other_position,
            offset=offset,
            grid_offset=grid_offset,
        )

    def check_overlap(
        self,
        other: "SimulationObject",
    ) -> bool:
        for axis in range(3):
            s_start, s_end = self._grid_slice_tuple[axis]
            o_start, o_end = other._grid_slice_tuple[axis]
            if o_start <= s_start <= o_end:
                return True
            if o_start <= s_end <= o_end:
                return True
        return False

    def __eq__(
        self: Self,
        other: "SimulationObject",
    ) -> bool:
        return self.name == other.name

    def __hash__(self) -> int:
        return hash(self.name)

extend_to(other, axis, direction, other_position=None, offset=0, grid_offset=0)

Creates a SizeExtensionConstraint that extends this object along a specified axis until it reaches another object or the simulation boundary. The extension can be in either positive or negative direction.

Parameters:

Name Type Description Default
other Optional[SimulationObject]

Target object to extend to, or None to extend to simulation boundary

required
axis int

Which axis to extend along (0, 1, or 2)

required
direction Literal['+', '-']

Direction to extend in ('+' or '-')

required
other_position float | None

Relative position on target object (-1 to 1) to extend to. If None, defaults to the corresponding side (-1 for '+' direction, 1 for '-' direction)

None
offset float

Additional offset in meters to apply after extension. Ignored when extending to simulation boundary

0
grid_offset int

Additional offset in Yee-grid voxels to apply after extension. Ignored when extending to simulation boundary

0

Returns:

Name Type Description
SizeExtensionConstraint SizeExtensionConstraint

Constraint defining how the object extends

Source code in src/fdtdx/objects/object.py
def extend_to(
    self,
    other: Optional["SimulationObject"],
    axis: int,
    direction: Literal["+", "-"],
    other_position: float | None = None,
    offset: float = 0,
    grid_offset: int = 0,
) -> SizeExtensionConstraint:
    """Creates a SizeExtensionConstraint that extends this object along a specified axis until it
    reaches another object or the simulation boundary. The extension can be in either positive or
    negative direction.

    Args:
        other: Target object to extend to, or None to extend to simulation boundary
        axis: Which axis to extend along (0, 1, or 2)
        direction: Direction to extend in ('+' or '-')
        other_position: Relative position on target object (-1 to 1) to extend to.
            If None, defaults to the corresponding side (-1 for '+' direction, 1 for '-' direction)
        offset: Additional offset in meters to apply after extension. Ignored when extending to
            simulation boundary
        grid_offset: Additional offset in Yee-grid voxels to apply after extension. Ignored when
            extending to simulation boundary

    Returns:
        SizeExtensionConstraint: Constraint defining how the object extends
    """
    # default: extend to corresponding side
    if other_position is None:
        other_position = -1 if direction == "+" else 1
    if other is None:
        if offset != 0 or grid_offset != 0:
            raise Exception("Cannot use offset when extending object to infinity")
    return SizeExtensionConstraint(
        object=self,
        other_object=other,
        axis=axis,
        direction=direction,
        other_position=other_position,
        offset=offset,
        grid_offset=grid_offset,
    )

face_to_face_negative_direction(other, axes, margins=None, grid_margins=None)

Creates a PositionConstraint that places this object facing another object in the negative direction of specified axes. The objects will touch at their facing boundaries unless margins are specified.

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to align on

required
margins tuple[float, ...] | float | None

Additional margins in meters between the facing surfaces. Must have same length as axes. If None, defaults to 0

None
grid_margins tuple[int, ...] | int | None

Additional margins in Yee-grid voxels between the facing surfaces. Must have same length as axes. If None, defaults to 0

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Position constraint aligning objects face-to-face in negative direction

Source code in src/fdtdx/objects/object.py
def face_to_face_negative_direction(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int,
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionConstraint that places this object facing another object in the negative direction
    of specified axes. The objects will touch at their facing boundaries unless margins are specified.

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes to align on
        margins: Additional margins in meters between the facing surfaces. Must have same length as axes.
            If None, defaults to 0
        grid_margins: Additional margins in Yee-grid voxels between the facing surfaces.
            Must have same length as axes. If None, defaults to 0

    Returns:
        PositionConstraint: Position constraint aligning objects face-to-face in negative direction
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    own_positions = tuple([1 for _ in axes])
    other_positions = tuple([-1 for _ in axes])
    constraint = self.place_relative_to(
        other=other,
        axes=axes,
        own_positions=own_positions,
        other_positions=other_positions,
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

face_to_face_positive_direction(other, axes, margins=None, grid_margins=None)

Creates a PositionConstraint that places this object facing another object in the positive direction of specified axes. The objects will touch at their facing boundaries unless margins are specified.

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to align on

required
margins tuple[float, ...] | float | None

Additional margins in meters between the facing surfaces. Must have same length as axes. If None, defaults to 0

None
grid_margins tuple[int, ...] | int | None

Additional margins in Yee-grid voxels between the facing surfaces. Must have same length as axes. If None, defaults to 0

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Position constraint aligning objects face-to-face in positive direction

Source code in src/fdtdx/objects/object.py
def face_to_face_positive_direction(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int,
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionConstraint that places this object facing another object in the positive direction
    of specified axes. The objects will touch at their facing boundaries unless margins are specified.

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes to align on
        margins: Additional margins in meters between the facing surfaces. Must have same length as axes.
            If None, defaults to 0
        grid_margins: Additional margins in Yee-grid voxels between the facing surfaces.
            Must have same length as axes. If None, defaults to 0

    Returns:
        PositionConstraint: Position constraint aligning objects face-to-face in positive direction
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    own_positions = tuple([-1 for _ in axes])
    other_positions = tuple([1 for _ in axes])
    constraint = self.place_relative_to(
        other=other,
        axes=axes,
        own_positions=own_positions,
        other_positions=other_positions,
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

place_above(other, margins=None, grid_margins=None)

Creates a PositionConstraint that places this object above another object along the z-axis. This is a convenience wrapper around face_to_face_positive_direction() for axis 2 (z-axis).

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
margins tuple[float, ...] | float | None

Additional vertical margins in meters between objects. If None, defaults to 0

None
grid_margins tuple[int, ...] | int | None

Additional vertical margins in Yee-grid voxels between objects. If None, defaults to 0

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Position constraint placing this object above the other

Source code in src/fdtdx/objects/object.py
def place_above(
    self,
    other: "SimulationObject",
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionConstraint that places this object above another object along the z-axis.
    This is a convenience wrapper around face_to_face_positive_direction() for axis 2 (z-axis).

    Args:
        other: Another object in the simulation scene
        margins: Additional vertical margins in meters between objects. If None, defaults to 0
        grid_margins: Additional vertical margins in Yee-grid voxels between objects.
            If None, defaults to 0

    Returns:
        PositionConstraint: Position constraint placing this object above the other
    """
    constraint = self.face_to_face_positive_direction(
        other=other,
        axes=(2,),
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

place_at_center(other, axes=(0, 1, 2), own_positions=None, other_positions=None, margins=None, grid_margins=None)

Creates a PositionConstraint that centers this object relative to another object along specified axes. This is a convenience wrapper around place_relative_to() with default positions at the center (0).

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to center on. Defaults to all axes (0, 1, 2)

(0, 1, 2)
own_positions tuple[float, ...] | float | None

Relative positions on this object (-1 to 1). If None, defaults to center (0)

None
other_positions tuple[float, ...] | float | None

Relative positions on other object (-1 to 1). If None, defaults to center (0)

None
margins tuple[float, ...] | float | None

Additional margins in meters between objects. Must have same length as axes. If None, defaults to 0

None
grid_margins tuple[int, ...] | int | None

Additional margins in Yee-grid voxels between objects. Must have same length as axes. If None, defaults to 0

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Position constraint centering objects relative to each other

Source code in src/fdtdx/objects/object.py
def place_at_center(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int = (0, 1, 2),
    own_positions: tuple[float, ...] | float | None = None,
    other_positions: tuple[float, ...] | float | None = None,
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionConstraint that centers this object relative to another object along specified axes.
    This is a convenience wrapper around place_relative_to() with default positions at the center (0).

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes to center on.
            Defaults to all axes (0, 1, 2)
        own_positions: Relative positions on this object (-1 to 1). If None, defaults to center (0)
        other_positions: Relative positions on other object (-1 to 1). If None, defaults to center (0)
        margins: Additional margins in meters between objects. Must have same length as axes.
            If None, defaults to 0
        grid_margins: Additional margins in Yee-grid voxels between objects. Must have same length as axes.
            If None, defaults to 0

    Returns:
        PositionConstraint: Position constraint centering objects relative to each other
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    if isinstance(own_positions, int | float):
        own_positions = tuple(
            [
                float(own_positions),
            ]
        )
    if isinstance(other_positions, int | float):
        other_positions = tuple(
            [
                float(other_positions),
            ]
        )
    if own_positions is None:
        own_positions = tuple([0 for _ in axes])
    if other_positions is None:
        other_positions = tuple([0 for _ in axes])
    constraint = self.place_relative_to(
        other=other,
        axes=axes,
        own_positions=own_positions,
        other_positions=other_positions,
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

place_below(other, margins=None, grid_margins=None)

Creates a PositionConstraint that places this object below another object along the z-axis. This is a convenience wrapper around face_to_face_negative_direction() for axis 2 (z-axis).

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
margins tuple[float, ...] | float | None

Additional vertical margins in meters between objects. If None, defaults to 0

None
grid_margins tuple[int, ...] | int | None

Additional vertical margins in Yee-grid voxels between objects. If None, defaults to 0

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Position constraint placing this object below the other

Source code in src/fdtdx/objects/object.py
def place_below(
    self,
    other: "SimulationObject",
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionConstraint that places this object below another object along the z-axis.
    This is a convenience wrapper around face_to_face_negative_direction() for axis 2 (z-axis).

    Args:
        other: Another object in the simulation scene
        margins: Additional vertical margins in meters between objects. If None, defaults to 0
        grid_margins: Additional vertical margins in Yee-grid voxels between objects.
            If None, defaults to 0

    Returns:
        PositionConstraint: Position constraint placing this object below the other
    """
    constraint = self.face_to_face_negative_direction(
        other=other,
        axes=(2,),
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

place_relative_to(other, axes, own_positions, other_positions, margins=None, grid_margins=None)

Creates a PositionalConstraint between two objects. The constraint is defined by anchor points on both objects, which are constrainted to be at the same position. Anchors are defined in relative coordinates, i.e. a position of -1 is the left object boundary in the repective axis and a position of +1 the right boundary.

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Eiter a single integer or a tuple describing the axes of the constraints

required
own_positions tuple[float, ...] | float

The positions of the own anchor in the axes. Must have the same lengths as axes

required
other_positions tuple[float, ...] | float

The positions of the other objects' anchor in the axes. Must have the same lengths as axes

required
margins tuple[float, ...] | float | None

The margins between the anchors of both objects in meters. Must have the same lengths as axes. Defaults to no margin

None
grid_margins tuple[int, ...] | int | None

The margins between the anchors of both objects in Yee-grid voxels. Must have the same lengths as axes. Defaults to no margin

None

Returns:

Name Type Description
PositionConstraint PositionConstraint

Positional constraint between this object and the other

Source code in src/fdtdx/objects/object.py
def place_relative_to(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int,
    own_positions: tuple[float, ...] | float,
    other_positions: tuple[float, ...] | float,
    margins: tuple[float, ...] | float | None = None,
    grid_margins: tuple[int, ...] | int | None = None,
) -> PositionConstraint:
    """Creates a PositionalConstraint between two objects. The constraint is defined by anchor points on
    both objects, which are constrainted to be at the same position. Anchors are defined in relative coordinates,
    i.e. a position of -1 is the left object boundary in the repective axis and a position of +1 the right boundary.

    Args:
        other: Another object in the simulation scene
        axes: Eiter a single integer or a tuple describing the axes of the constraints
        own_positions: The positions of the own anchor in the axes. Must have the same lengths as axes
        other_positions: The positions of the other objects' anchor in the axes. Must have the same lengths as axes
        margins: The margins between the anchors of both objects in meters. Must have the same lengths as axes. Defaults to no margin
        grid_margins: The margins between the anchors of both objects in Yee-grid voxels. Must have the same lengths as axes. Defaults to no margin

    Returns:
        PositionConstraint: Positional constraint between this object and the other
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    if isinstance(own_positions, int | float):
        own_positions = tuple(
            [
                float(own_positions),
            ]
        )
    if isinstance(other_positions, int | float):
        other_positions = tuple(
            [
                float(other_positions),
            ]
        )
    if isinstance(margins, int | float):
        margins = tuple(
            [
                float(margins),
            ]
        )
    if isinstance(grid_margins, int):
        grid_margins = tuple(
            [
                grid_margins,
            ]
        )
    if margins is None:
        margins = tuple([0 for _ in axes])
    if grid_margins is None:
        grid_margins = tuple([0 for _ in axes])
    if (
        len(axes) != len(own_positions)
        or len(axes) != len(other_positions)
        or len(axes) != len(margins)
        or len(axes) != len(grid_margins)
    ):
        raise Exception("All inputs should have same lengths")
    constraint = PositionConstraint(
        axes=axes,
        other_object=other,
        object=self,
        other_object_positions=other_positions,
        object_positions=own_positions,
        margins=margins,
        grid_margins=grid_margins,
    )
    return constraint

same_position_and_size(other, axes=(0, 1, 2))

Creates both position and size constraints to make this object match another object's position and size. This is a convenience wrapper combining place_at_center() and same_size().

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to match. Defaults to all axes (0, 1, 2)

(0, 1, 2)

Returns:

Type Description
tuple[PositionConstraint, SizeConstraint]

tuple[PositionConstraint, SizeConstraint]: Position and size constraints for matching objects

Source code in src/fdtdx/objects/object.py
def same_position_and_size(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int = (0, 1, 2),
) -> tuple[PositionConstraint, SizeConstraint]:
    """Creates both position and size constraints to make this object match another object's position and size.
    This is a convenience wrapper combining place_at_center() and same_size().

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes to match.
            Defaults to all axes (0, 1, 2)

    Returns:
        tuple[PositionConstraint, SizeConstraint]: Position and size constraints for matching objects
    """
    size_constraint = self.same_size(
        other=other,
        axes=axes,
    )
    pos_constraint = self.place_at_center(
        other=other,
        axes=axes,
    )
    return pos_constraint, size_constraint

same_size(other, axes=(0, 1, 2), offsets=None, grid_offsets=None)

Creates a SizeConstraint that makes this object the same size as another object along specified axes. This is a convenience wrapper around size_relative_to() with proportions set to 1.0.

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes should have the same size. Defaults to all axes (0, 1, 2)

(0, 1, 2)
offsets tuple[float, ...] | float | None

Additional size offsets in meters to apply. Must have same length as axes. If None, defaults to 0

None
grid_offsets tuple[int, ...] | int | None

Additional size offsets in Yee-grid voxels to apply. Must have same length as axes. If None, defaults to 0

None

Returns:

Name Type Description
SizeConstraint SizeConstraint

Size constraint ensuring equal sizes between objects

Source code in src/fdtdx/objects/object.py
def same_size(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int = (0, 1, 2),
    offsets: tuple[float, ...] | float | None = None,
    grid_offsets: tuple[int, ...] | int | None = None,
) -> SizeConstraint:
    """Creates a SizeConstraint that makes this object the same size as another object along specified axes.
    This is a convenience wrapper around size_relative_to() with proportions set to 1.0.

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes should have the same size.
            Defaults to all axes (0, 1, 2)
        offsets: Additional size offsets in meters to apply. Must have same length as axes.
            If None, defaults to 0
        grid_offsets: Additional size offsets in Yee-grid voxels to apply. Must have same length as axes.
            If None, defaults to 0

    Returns:
        SizeConstraint: Size constraint ensuring equal sizes between objects
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    proportions = tuple([1 for _ in axes])
    constraint = self.size_relative_to(
        other=other,
        axes=axes,
        proportions=proportions,
        offsets=offsets,
        grid_offsets=grid_offsets,
    )
    return constraint

set_grid_coordinates(axes, sides, coordinates)

Creates a GridCoordinateConstraint that forces specific sides of this object to align with given grid coordinates. Used for precise positioning in the discretized simulation space.

Parameters:

Name Type Description Default
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to constrain

required
sides tuple[Literal['+', '-'], ...] | Literal['+', '-']

Either a single string or a tuple of strings ('+' or '-') indicating which side of each axis to constrain. Must have same length as axes

required
coordinates tuple[int, ...] | int

Either a single integer or a tuple of integers specifying the grid coordinates to align with. Must have same length as axes

required

Returns:

Name Type Description
GridCoordinateConstraint GridCoordinateConstraint

Constraint forcing alignment with specific grid coordinates

Source code in src/fdtdx/objects/object.py
def set_grid_coordinates(
    self,
    axes: tuple[int, ...] | int,
    sides: tuple[Literal["+", "-"], ...] | Literal["+", "-"],
    coordinates: tuple[int, ...] | int,
) -> GridCoordinateConstraint:
    """Creates a GridCoordinateConstraint that forces specific sides of this object to align with
    given grid coordinates. Used for precise positioning in the discretized simulation space.

    Args:
        axes: Either a single integer or a tuple describing which axes to constrain
        sides: Either a single string or a tuple of strings ('+' or '-') indicating which side
            of each axis to constrain. Must have same length as axes
        coordinates: Either a single integer or a tuple of integers specifying the grid coordinates
            to align with. Must have same length as axes

    Returns:
        GridCoordinateConstraint: Constraint forcing alignment with specific grid coordinates
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    if isinstance(sides, str):
        sides = (sides,)
    if isinstance(coordinates, int):
        coordinates = tuple(
            [
                coordinates,
            ]
        )
    if len(axes) != len(sides) or len(axes) != len(coordinates):
        raise Exception("All inputs need to have the same lengths!")
    return GridCoordinateConstraint(
        object=self,
        axes=axes,
        sides=sides,
        coordinates=coordinates,
    )

set_real_coordinates(axes, sides, coordinates)

Creates a RealCoordinateConstraint that forces specific sides of this object to align with given real-space coordinates. Used for precise positioning in physical units.

Parameters:

Name Type Description Default
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes to constrain

required
sides tuple[Literal['+', '-'], ...] | Literal['+', '-']

Either a single string or a tuple of strings ('+' or '-') indicating which side of each axis to constrain. Must have same length as axes

required
coordinates tuple[float, ...] | float

Either a single float or a tuple of floats specifying the real-space coordinates in meters to align with. Must have same length as axes

required

Returns:

Name Type Description
RealCoordinateConstraint RealCoordinateConstraint

Constraint forcing alignment with specific real-space coordinates

Source code in src/fdtdx/objects/object.py
def set_real_coordinates(
    self,
    axes: tuple[int, ...] | int,
    sides: tuple[Literal["+", "-"], ...] | Literal["+", "-"],
    coordinates: tuple[float, ...] | float,
) -> RealCoordinateConstraint:
    """Creates a RealCoordinateConstraint that forces specific sides of this object to align with
    given real-space coordinates. Used for precise positioning in physical units.

    Args:
        axes: Either a single integer or a tuple describing which axes to constrain
        sides: Either a single string or a tuple of strings ('+' or '-') indicating which side
            of each axis to constrain. Must have same length as axes
        coordinates: Either a single float or a tuple of floats specifying the real-space coordinates
            in meters to align with. Must have same length as axes

    Returns:
        RealCoordinateConstraint: Constraint forcing alignment with specific real-space coordinates
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    if isinstance(sides, str):
        sides = (sides,)
    if isinstance(coordinates, int | float):
        coordinates = tuple(
            [
                float(coordinates),
            ]
        )
    if len(axes) != len(sides) or len(axes) != len(coordinates):
        raise Exception("All inputs need to have the same lengths!")
    return RealCoordinateConstraint(
        object=self,
        axes=axes,
        sides=sides,
        coordinates=coordinates,
    )

size_relative_to(other, axes, other_axes=None, proportions=None, offsets=None, grid_offsets=None)

Creates a SizeConstraint between two objects. The constraint defines the size of this object relative to another object, allowing for proportional scaling and offsets in specified axes.

Parameters:

Name Type Description Default
other SimulationObject

Another object in the simulation scene

required
axes tuple[int, ...] | int

Either a single integer or a tuple describing which axes of this object to constrain

required
other_axes tuple[int, ...] | int | None

Either a single integer or a tuple describing which axes of the other object to reference. If None, uses the same axes as specified in 'axes'

None
proportions tuple[float, ...] | float | None

Scale factors to apply to the other object's dimensions. Must have same length as axes. If None, defaults to 1.0 (same size)

None
offsets tuple[float, ...] | float | None

Additional size offsets in meters to apply after scaling. Must have same length as axes. If None, defaults to 0

None
grid_offsets tuple[int, ...] | int | None

Additional size offsets in Yee-grid voxels to apply after scaling. Must have same length as axes. If None, defaults to 0

None

Returns:

Name Type Description
SizeConstraint SizeConstraint

Size constraint between this object and the other

Source code in src/fdtdx/objects/object.py
def size_relative_to(
    self,
    other: "SimulationObject",
    axes: tuple[int, ...] | int,
    other_axes: tuple[int, ...] | int | None = None,
    proportions: tuple[float, ...] | float | None = None,
    offsets: tuple[float, ...] | float | None = None,
    grid_offsets: tuple[int, ...] | int | None = None,
) -> SizeConstraint:
    """Creates a SizeConstraint between two objects. The constraint defines the size of this object relative
    to another object, allowing for proportional scaling and offsets in specified axes.

    Args:
        other: Another object in the simulation scene
        axes: Either a single integer or a tuple describing which axes of this object to constrain
        other_axes: Either a single integer or a tuple describing which axes of the other object to reference.
            If None, uses the same axes as specified in 'axes'
        proportions: Scale factors to apply to the other object's dimensions. Must have same length as axes.
            If None, defaults to 1.0 (same size)
        offsets: Additional size offsets in meters to apply after scaling. Must have same length as axes.
            If None, defaults to 0
        grid_offsets: Additional size offsets in Yee-grid voxels to apply after scaling. Must have same length as axes.
            If None, defaults to 0

    Returns:
        SizeConstraint: Size constraint between this object and the other
    """
    if isinstance(axes, int):
        axes = tuple(
            [
                axes,
            ]
        )
    if isinstance(other_axes, int):
        other_axes = tuple(
            [
                other_axes,
            ]
        )
    if isinstance(proportions, int | float):
        proportions = tuple(
            [
                float(proportions),
            ]
        )
    if isinstance(offsets, int | float):
        offsets = tuple(
            [
                float(offsets),
            ]
        )
    if isinstance(grid_offsets, int):
        grid_offsets = tuple(
            [
                grid_offsets,
            ]
        )
    if offsets is None:
        offsets = tuple([0 for _ in axes])
    if grid_offsets is None:
        grid_offsets = tuple([0 for _ in axes])
    if proportions is None:
        proportions = tuple([1.0 for _ in axes])
    if other_axes is None:
        other_axes = tuple([a for a in axes])
    if len(axes) != len(proportions) or len(axes) != len(offsets) or len(axes) != len(grid_offsets):
        raise Exception("All inputs should have same lengths")
    constraint = SizeConstraint(
        other_object=other,
        object=self,
        axes=axes,
        other_axes=other_axes,
        proportions=proportions,
        offsets=offsets,
        grid_offsets=grid_offsets,
    )
    return constraint

Base class for all simulation objects with positioning and sizing capabilities.

fdtdx.objects.object.UniqueName

Bases: ExtendedTreeClass

Generates unique names for simulation objects.

A utility class that ensures each simulation object gets a unique name by maintaining a global counter. If no name is provided, generates names in the format "Object_N" where N is an incrementing counter.

Source code in src/fdtdx/objects/object.py
@tc.autoinit
class UniqueName(ExtendedTreeClass):
    """Generates unique names for simulation objects.

    A utility class that ensures each simulation object gets a unique name by
    maintaining a global counter. If no name is provided, generates names in
    the format "Object_N" where N is an incrementing counter.
    """

    def __call__(self, x: str | None) -> str:
        """Generate a unique name if none is provided.

        Args:
            x: The proposed name or None

        Returns:
            str: Either the input name if provided, or a new unique name
        """
        global _GLOBAL_COUNTER
        if x is None:
            name = f"Object_{_GLOBAL_COUNTER}"
            _GLOBAL_COUNTER += 1
            return name
        return x

__call__(x)

Generate a unique name if none is provided.

Parameters:

Name Type Description Default
x str | None

The proposed name or None

required

Returns:

Name Type Description
str str

Either the input name if provided, or a new unique name

Source code in src/fdtdx/objects/object.py
def __call__(self, x: str | None) -> str:
    """Generate a unique name if none is provided.

    Args:
        x: The proposed name or None

    Returns:
        str: Either the input name if provided, or a new unique name
    """
    global _GLOBAL_COUNTER
    if x is None:
        name = f"Object_{_GLOBAL_COUNTER}"
        _GLOBAL_COUNTER += 1
        return name
    return x

Utility for generating unique object identifiers.

Positioning Constraints

fdtdx.objects.object.PositionConstraint dataclass

Defines a positional relationship between two simulation objects.

A constraint that positions one object relative to another, with optional margins and offsets. Used to specify how objects should be placed in the simulation volume relative to each other.

Attributes:

Name Type Description
object SimulationObject

The "child" object whose position is being adjusted

other_object SimulationObject

The "parent" object that serves as reference

axes tuple[int, ...]

Which axes (x,y,z) this constraint applies to

object_positions tuple[float, ...]

Relative positions on child object (-1 to 1)

other_object_positions tuple[float, ...]

Relative positions on parent object (-1 to 1)

margins tuple[float, ...]

Optional real-space margins between objects

grid_margins tuple[int, ...]

Optional grid-space margins between objects

Source code in src/fdtdx/objects/object.py
@dataclass(kw_only=True, frozen=True)
class PositionConstraint:
    """Defines a positional relationship between two simulation objects.

    A constraint that positions one object relative to another, with optional
    margins and offsets. Used to specify how objects should be placed in the
    simulation volume relative to each other.

    Attributes:
        object: The "child" object whose position is being adjusted
        other_object: The "parent" object that serves as reference
        axes: Which axes (x,y,z) this constraint applies to
        object_positions: Relative positions on child object (-1 to 1)
        other_object_positions: Relative positions on parent object (-1 to 1)
        margins: Optional real-space margins between objects
        grid_margins: Optional grid-space margins between objects
    """

    object: "SimulationObject"  # "child" object, whose pos is adjusted
    other_object: "SimulationObject"  # "parent" object
    axes: tuple[int, ...]
    object_positions: tuple[float, ...]
    other_object_positions: tuple[float, ...]
    margins: tuple[float, ...]
    grid_margins: tuple[int, ...]

Defines relative positioning between simulation objects.

fdtdx.objects.object.SizeConstraint dataclass

Defines a size relationship between two simulation objects.

A constraint that sets the size of one object relative to another, with optional proportions and offsets. Used to specify how objects should be sized relative to each other in the simulation.

Attributes:

Name Type Description
object SimulationObject

The "child" object whose size is being adjusted

other_object SimulationObject

The "parent" object that serves as reference

axes tuple[int, ...]

Which axes of the child to constrain

other_axes tuple[int, ...]

Which axes of the parent to reference

proportions tuple[float, ...]

Size multipliers relative to parent

offsets tuple[float, ...]

Additional real-space size offsets

grid_offsets tuple[int, ...]

Additional grid-space size offsets

Source code in src/fdtdx/objects/object.py
@dataclass(kw_only=True, frozen=True)
class SizeConstraint:
    """Defines a size relationship between two simulation objects.

    A constraint that sets the size of one object relative to another, with
    optional proportions and offsets. Used to specify how objects should be
    sized relative to each other in the simulation.

    Attributes:
        object: The "child" object whose size is being adjusted
        other_object: The "parent" object that serves as reference
        axes: Which axes of the child to constrain
        other_axes: Which axes of the parent to reference
        proportions: Size multipliers relative to parent
        offsets: Additional real-space size offsets
        grid_offsets: Additional grid-space size offsets
    """

    object: "SimulationObject"  # "child" object, whose size is adjusted
    other_object: "SimulationObject"  # "parent" object
    axes: tuple[int, ...]
    other_axes: tuple[int, ...]
    proportions: tuple[float, ...]
    offsets: tuple[float, ...]
    grid_offsets: tuple[int, ...]

Controls size relationships between objects.

fdtdx.objects.object.SizeExtensionConstraint dataclass

Defines how an object extends toward another object or boundary.

A constraint that extends one object's size until it reaches another object or the simulation boundary. Can extend in positive or negative direction along an axis.

Attributes:

Name Type Description
object SimulationObject

The object being extended

other_object Optional[SimulationObject]

Optional target object to extend to

axis int

Which axis to extend along

direction Literal['+', '-']

Direction to extend ('+' or '-')

other_position float

Relative position on target (-1 to 1)

offset float

Additional real-space offset

grid_offset int

Additional grid-space offset

Source code in src/fdtdx/objects/object.py
@dataclass(kw_only=True, frozen=True)
class SizeExtensionConstraint:
    """Defines how an object extends toward another object or boundary.

    A constraint that extends one object's size until it reaches another object
    or the simulation boundary. Can extend in positive or negative direction
    along an axis.

    Attributes:
        object: The object being extended
        other_object: Optional target object to extend to
        axis: Which axis to extend along
        direction: Direction to extend ('+' or '-')
        other_position: Relative position on target (-1 to 1)
        offset: Additional real-space offset
        grid_offset: Additional grid-space offset
    """

    object: "SimulationObject"  # "child" object, whose size is adjusted
    other_object: Optional["SimulationObject"]  # "parent" object
    axis: int
    direction: Literal["+", "-"]
    other_position: float
    offset: float
    grid_offset: int

Extends objects to reach other objects or boundaries.

fdtdx.objects.object.GridCoordinateConstraint dataclass

Constrains an object's position to specific grid coordinates.

Forces specific sides of an object to align with given grid coordinates. Used for precise positioning in the discretized simulation space.

Attributes:

Name Type Description
object SimulationObject

The object to position

axes tuple[int, ...]

Which axes to constrain

sides tuple[Literal['+', '-'], ...]

Which side of each axis ('+' or '-')

coordinates tuple[int, ...]

Grid coordinates to align with

Source code in src/fdtdx/objects/object.py
@dataclass(kw_only=True, frozen=True)
class GridCoordinateConstraint:
    """Constrains an object's position to specific grid coordinates.

    Forces specific sides of an object to align with given grid coordinates.
    Used for precise positioning in the discretized simulation space.

    Attributes:
        object: The object to position
        axes: Which axes to constrain
        sides: Which side of each axis ('+' or '-')
        coordinates: Grid coordinates to align with
    """

    object: "SimulationObject"
    axes: tuple[int, ...]
    sides: tuple[Literal["+", "-"], ...]
    coordinates: tuple[int, ...]

Aligns objects to specific grid coordinates.

fdtdx.objects.object.RealCoordinateConstraint dataclass

Constrains an object's position to specific real-space coordinates.

Forces specific sides of an object to align with given real-space coordinates. Used for precise positioning in physical units.

Attributes:

Name Type Description
object SimulationObject

The object to position

axes tuple[int, ...]

Which axes to constrain

sides tuple[Literal['+', '-'], ...]

Which side of each axis ('+' or '-')

coordinates tuple[float, ...]

Real-space coordinates to align with

Source code in src/fdtdx/objects/object.py
@dataclass(kw_only=True, frozen=True)
class RealCoordinateConstraint:
    """Constrains an object's position to specific real-space coordinates.

    Forces specific sides of an object to align with given real-space coordinates.
    Used for precise positioning in physical units.

    Attributes:
        object: The object to position
        axes: Which axes to constrain
        sides: Which side of each axis ('+' or '-')
        coordinates: Real-space coordinates to align with
    """

    object: "SimulationObject"
    axes: tuple[int, ...]
    sides: tuple[Literal["+", "-"], ...]
    coordinates: tuple[float, ...]

Positions objects at specific physical coordinates.

Wavelength-Dependent Objects

fdtdx.objects.wavelength.WaveLengthDependentObject

Bases: SimulationObject, ABC

Base class for objects whose properties depend on wavelength/period.

An abstract base class for simulation objects that have wavelength-dependent behavior. Provides properties to handle either wavelength or period specification, ensuring only one is set at a time.

Attributes:

Name Type Description
_period float | None

Optional period in seconds. Mutually exclusive with _wavelength.

_wavelength float | None

Optional wavelength in meters. Mutually exclusive with _period.

Source code in src/fdtdx/objects/wavelength.py
@tc.autoinit
class WaveLengthDependentObject(SimulationObject, ABC):
    """Base class for objects whose properties depend on wavelength/period.

    An abstract base class for simulation objects that have wavelength-dependent
    behavior. Provides properties to handle either wavelength or period specification,
    ensuring only one is set at a time.

    Attributes:
        _period: Optional period in seconds. Mutually exclusive with _wavelength.
        _wavelength: Optional wavelength in meters. Mutually exclusive with _period.
    """

    _period: float | None = tc.field(default=None, alias="period")  # type: ignore
    _wavelength: float | None = tc.field(default=None, alias="wavelength")  # type: ignore

    @property
    def period(self) -> float:
        """Gets the period in seconds.

        Returns:
            float: The period in seconds, either directly set or computed from wavelength.

        Raises:
            Exception: If neither period nor wavelength is set, or if both are set.
        """
        if self._period is not None and self._wavelength is not None:
            raise Exception("Need to set either wavelength or period")
        if self._period is not None:
            return self._period
        if self._wavelength is not None:
            return self._wavelength / constants.c
        raise Exception("Need to set either wavelength or period")

    @property
    def wavelength(self) -> float:
        """Gets the wavelength in meters.

        Returns:
            float: The wavelength in meters, either directly set or computed from period.

        Raises:
            Exception: If neither period nor wavelength is set, or if both are set.
        """
        if self._period is not None and self._wavelength is not None:
            raise Exception("Need to set either wavelength or period")
        if self._wavelength is not None:
            return self._wavelength
        if self._period is not None:
            return self._period * constants.c
        raise Exception("Need to set either wavelength or period")

    @property
    def frequency(self) -> float:
        """Gets the frequency in Hz.

        Returns:
            float: The frequency in Hz, computed as 1/period.
        """
        return 1 / self.period

frequency: float property

Gets the frequency in Hz.

Returns:

Name Type Description
float float

The frequency in Hz, computed as 1/period.

period: float property

Gets the period in seconds.

Returns:

Name Type Description
float float

The period in seconds, either directly set or computed from wavelength.

Raises:

Type Description
Exception

If neither period nor wavelength is set, or if both are set.

wavelength: float property

Gets the wavelength in meters.

Returns:

Name Type Description
float float

The wavelength in meters, either directly set or computed from period.

Raises:

Type Description
Exception

If neither period nor wavelength is set, or if both are set.

Base class for objects with wavelength/period-dependent properties.

fdtdx.objects.wavelength.WaveLengthDependentNoMaterial

Bases: WaveLengthDependentObject

A wavelength-dependent object that doesn't modify material properties.

Implements WaveLengthDependentObject for cases where the object doesn't affect the permittivity or permeability of the simulation volume.

Source code in src/fdtdx/objects/wavelength.py
@tc.autoinit
class WaveLengthDependentNoMaterial(WaveLengthDependentObject):
    """A wavelength-dependent object that doesn't modify material properties.

    Implements WaveLengthDependentObject for cases where the object doesn't affect
    the permittivity or permeability of the simulation volume.
    """

    def get_inv_permittivity(
        self,
        prev_inv_permittivity: jax.Array,
        params: dict[str, jax.Array] | None,
    ) -> tuple[jax.Array, dict]:  # permittivity and info dict
        """Returns unchanged inverse permittivity.

        Args:
            prev_inv_permittivity: The existing inverse permittivity array
            params: Optional parameter dictionary

        Returns:
            tuple: (Unchanged inverse permittivity array, Empty info dict)
        """
        del params
        return prev_inv_permittivity, {}

    def get_inv_permeability(
        self,
        prev_inv_permeability: jax.Array,
        params: dict[str, jax.Array] | None,
    ) -> tuple[jax.Array, dict]:  # permeability and info dict
        """Returns unchanged inverse permeability.

        Args:
            prev_inv_permeability: The existing inverse permeability array
            params: Optional parameter dictionary

        Returns:
            tuple: (Unchanged inverse permeability array, Empty info dict)
        """
        del params
        return prev_inv_permeability, {}

get_inv_permeability(prev_inv_permeability, params)

Returns unchanged inverse permeability.

Parameters:

Name Type Description Default
prev_inv_permeability Array

The existing inverse permeability array

required
params dict[str, Array] | None

Optional parameter dictionary

required

Returns:

Name Type Description
tuple tuple[Array, dict]

(Unchanged inverse permeability array, Empty info dict)

Source code in src/fdtdx/objects/wavelength.py
def get_inv_permeability(
    self,
    prev_inv_permeability: jax.Array,
    params: dict[str, jax.Array] | None,
) -> tuple[jax.Array, dict]:  # permeability and info dict
    """Returns unchanged inverse permeability.

    Args:
        prev_inv_permeability: The existing inverse permeability array
        params: Optional parameter dictionary

    Returns:
        tuple: (Unchanged inverse permeability array, Empty info dict)
    """
    del params
    return prev_inv_permeability, {}

get_inv_permittivity(prev_inv_permittivity, params)

Returns unchanged inverse permittivity.

Parameters:

Name Type Description Default
prev_inv_permittivity Array

The existing inverse permittivity array

required
params dict[str, Array] | None

Optional parameter dictionary

required

Returns:

Name Type Description
tuple tuple[Array, dict]

(Unchanged inverse permittivity array, Empty info dict)

Source code in src/fdtdx/objects/wavelength.py
def get_inv_permittivity(
    self,
    prev_inv_permittivity: jax.Array,
    params: dict[str, jax.Array] | None,
) -> tuple[jax.Array, dict]:  # permittivity and info dict
    """Returns unchanged inverse permittivity.

    Args:
        prev_inv_permittivity: The existing inverse permittivity array
        params: Optional parameter dictionary

    Returns:
        tuple: (Unchanged inverse permittivity array, Empty info dict)
    """
    del params
    return prev_inv_permittivity, {}

Non-material modifying wavelength-dependent object.