1414from magpylib ._src .obj_classes .class_magnet_Cylinder import Cylinder
1515from magpylib ._src .obj_classes .class_magnet_CylinderSegment import CylinderSegment
1616from magpylib ._src .obj_classes .class_magnet_Sphere import Sphere
17+ from magpylib ._src .obj_classes .class_misc_Dipole import Dipole
1718
1819from magpylib_force .meshing import mesh_target
1920from magpylib_force .utility import check_input_anchor , check_input_targets
@@ -35,8 +36,8 @@ def getFT(sources, targets, anchor=None, eps=1e-5, squeeze=True):
3536 or a 1D list of l sources and/or collection objects.
3637
3738 targets: single object or 1D list of t objects that are Sphere, Cuboid, Polyline,
38- Cylinder, or CylinderSegment . Force and Torque acting on targets in the magnetic
39- field generated by the sources will be computed. A target must have a valid
39+ Cylinder, CylinderSegment, or Dipole . Force and Torque acting on targets in the magnetic
40+ field generated by the sources will be computed. A target (except of Dipole) must have a valid
4041 `meshing` parameter.
4142
4243 anchor: array_like, default=None
@@ -65,14 +66,15 @@ def getFT(sources, targets, anchor=None, eps=1e-5, squeeze=True):
6566 n = len (targets )
6667
6768 # split targets into lists of similar types
68- TARGET_TYPES = [Cuboid , Polyline , Sphere , Cylinder , CylinderSegment , Circle ]
69+ TARGET_TYPES = [Cuboid , Polyline , Sphere , Cylinder , CylinderSegment , Circle , Dipole ]
6970 getFT_FUNCS = [
7071 getFTmagnet ,
7172 getFTcurrent ,
7273 getFTmagnet ,
7374 getFTmagnet ,
7475 getFTmagnet ,
7576 getFTcurrent_circ ,
77+ getFTdipole ,
7678 ]
7779 objects = [[] for _ in TARGET_TYPES ]
7880 orders = [[] for _ in TARGET_TYPES ]
@@ -202,6 +204,8 @@ def getFTmagnet(sources, targets, eps=1e-5, anchor=None):
202204 POSS [insti [i ] : insti [i + 1 ], j ] = mesh + ev + tgt .position
203205
204206 BB = magpy .getB (sources , POSS , sumup = True )
207+ if BB .ndim == 2 :
208+ BB = np .expand_dims (BB , axis = 0 )
205209 gradB = (BB [:, 1 ::2 ] - BB [:, 2 ::2 ]) / (2 * eps )
206210 gradB = np .swapaxes (gradB , 0 , 1 )
207211
@@ -333,3 +337,71 @@ def getFTcurrent(sources, targets, anchor=None, eps=None): # noqa: ARG001
333337 )
334338
335339 return np .array ((F , - T ))
340+
341+
342+ def getFTdipole (sources , targets , anchor = None , eps = None ):
343+ """
344+ Compute force and torque acting on magnetic dipoles
345+
346+ Parameters
347+ ----------
348+ sources: source and collection objects or 1D list thereof
349+ Sources that generate the magnetic field. Can be a single source (or collection)
350+ or a 1D list of l sources and/or collection objects.
351+
352+ targets: Dipole object or 1D list of Dipole objects
353+ Force and Torque acting on targets in the magnetic field generated by the sources
354+ will be computed.
355+
356+ eps: float, default=1e-5
357+ The magnetic field gradient is computed using finite differences (FD). eps is
358+ the FD step size. A good value is 1e-5 * characteristic system size (magnet size,
359+ distance between sources and targets, ...).
360+
361+ anchor: array_like, default=None
362+ The Force adds to the Torque via the anchor point. For a freely floating magnet
363+ this would be the barycenter. If `anchor=None`, this part of the Torque computation
364+ is omitted.
365+ """
366+ # number of magnets
367+ tgt_number = len (targets )
368+
369+ # field computation positions (1xfor B, 6x for gradB)
370+ POSS = np .zeros ((tgt_number , 7 , 3 ))
371+
372+ # moment of each instance
373+ MOM = np .zeros ((tgt_number , 3 ))
374+
375+ # MISSING: eps should be defined relative to the sizes of the objects
376+ eps_vec = np .array (
377+ [
378+ (0 , 0 , 0 ),
379+ (eps , 0 , 0 ),
380+ (- eps , 0 , 0 ),
381+ (0 , eps , 0 ),
382+ (0 , - eps , 0 ),
383+ (0 , 0 , eps ),
384+ (0 , 0 , - eps ),
385+ ]
386+ )
387+
388+ for i , tgt in enumerate (targets ):
389+ dipole_mom = tgt .orientation .apply (tgt .moment )
390+ MOM [i ] = dipole_mom
391+
392+ for j , ev in enumerate (eps_vec ):
393+ POSS [i , j ] = ev + tgt .position
394+
395+ BB = magpy .getB (sources , POSS , sumup = True )
396+ if BB .ndim == 2 :
397+ BB = np .expand_dims (BB , axis = 0 )
398+ gradB = (BB [:, 1 ::2 ] - BB [:, 2 ::2 ]) / (2 * eps )
399+ gradB = np .swapaxes (gradB , 0 , 1 )
400+
401+ F = np .sum ((gradB * MOM ), axis = 2 ).T
402+ # Ts = np.zeros((no_inst,3))
403+ T = np .cross (BB [:, 0 ], MOM )
404+ if anchor is not None :
405+ T -= np .cross (POSS [:, 0 ] - anchor , F )
406+
407+ return np .array ((F , - T ))
0 commit comments