Compounds#

The Collection class is a powerful tool for grouping and tracking object assemblies. However, in many cases it is convenient to have assembly variables themselves (e.g. geometric arrangement) as class properties of new custom classes, which is achieved by sub-classing Collection. We refer to such super-classes as compounds and show how to seamlessly integrate them into Magpylib.

Subclassing collections#

In the following example we design a compound class MagnetRing which represents a ring of cuboid magnets with the parameter cubes that should refer to the number of magnets on the ring. The ring will automatically adjust its size when cubes is modified. In the spirit of Efficient 3D models we also add an encompassing 3D model.

import magpylib as magpy

class MagnetRing(magpy.Collection):
    """ A ring of cuboid magnets

    Parameters
    ----------
    cubes: int, default=6
        Number of cubes on ring.
    """

    def __init__(self, cubes=6, **style_kwargs):
        super().__init__(**style_kwargs)             # hand over style args
        self._update(cubes)

    @property
    def cubes(self):
        """ Number of cubes"""
        return self._cubes

    @cubes.setter
    def cubes(self, inp):
        """ set cubes"""
        self._update(inp)

    def _update(self, cubes):
        """updates the MagnetRing instance"""
        self._cubes = cubes
        ring_radius = cubes/3

        # construct in temporary Collection for path transfer
        temp_coll = magpy.Collection()
        for i in range(cubes):
            child = magpy.magnet.Cuboid(
                magnetization=(1000,0,0),
                dimension=(1,1,1),
                position=(ring_radius,0,0)
            )
            child.rotate_from_angax(360/cubes*i, 'z', anchor=0)
            temp_coll.add(child)

        # transfer path and children
        temp_coll.position = self.position
        temp_coll.orientation = self.orientation
        self.children = temp_coll.children

        # add parameter-dependent 3d trace
        self.style.model3d.data = []
        self.style.model3d.add_trace(self._custom_trace3d())

        return self

    def _custom_trace3d(self):
        """ creates a parameter-dependent 3d model"""
        r1 = self.cubes/3 - .6
        r2 = self.cubes/3 + 0.6
        trace = magpy.graphics.model3d.make_CylinderSegment(
            dimension=(r1, r2, 1.1, 0, 360),
            vert=150,
            opacity=0.5,
        )
        return trace

The new MagnetRing objects will seamlessly integrate into Magpylib and make use of the position and orientation interface, field computation and graphic display.

# add a sensor
sensor = magpy.Sensor(position=(0, 0, 0))

# create a MagnetRing object
ring = MagnetRing()

# move ring around
ring.position = (0,0,10)
ring.rotate_from_angax(angle=45, axis=(1,-1,0))

# compute field
print(f"B-field at sensor → {ring.getB(sensor).round(2)}")

# display graphically
magpy.show(ring, sensor, backend='plotly')
B-field at sensor → [0.1  0.1  0.08]