(docs-fieldcomp)= # Field Computation (B, H, J, M) The following is a detailed technical documentation of Magpylib field computation. The tutorial {ref}`examples-tutorial-field-computation` shows good practices and illustrative examples. ------------------------------------- (docs-fieldcomp-oo)= ## Object-oriented Interface The object-oriented interface relies on the idea that sources of the magnetic field and observers thereof are created as Python objects which can be manipulated at will, and called for field computation. This is done via four top-level functions `getB()`, `getH()`, `getJ()` and, `getM()`, ```python B = magpylib.getB(sources, observers, squeeze=True, pixel_agg=None, output="ndarray") H = magpylib.getH(sources, observers, squeeze=True, pixel_agg=None, output="ndarray") J = magpylib.getJ(sources, observers, squeeze=True, pixel_agg=None, output="ndarray") M = magpylib.getM(sources, observers, squeeze=True, pixel_agg=None, output="ndarray") ``` that compute the respective fields B (B-field), H (H-field), J (polarization) or M (magnetization) generated by `sources` as seen by the `observers` in their local coordinates. `sources` can be any Magpylib source object (e.g. magnets) or a flat list thereof. `observers` can be an array of position vectors with shape (o1, o2, ..., 3), any Magpylib observer object (e.g. sensors), or a flat list thereof. The following code shows a minimal example for Magpylib field computation. ```python import magpylib as magpy # Define source and observer objects loop = magpy.current.Circle(current=1, diameter=0.001) sens = magpy.Sensor() # Compute field B = magpy.getB(loop, sens) print(B) # --> [0. 0. 0.00125664] ``` For quick access, the functions `getBHJM` are also methods of all Magpylib objects, such that the `sources` or `observers` input is the object itself. The above example can be continued as ```python # Call getB as method of loop B = loop.getB(sens) # Call getB as method of loop B = sens.getB(loop) ``` with the same result for `B`. By default, `getB()` returns the B-field in units (T), `getH()` the H-field in units (A/m), `getJ()` the magnetic polarization in (T) and, `getM()` the magnetization in (A/m), assuming that all inputs are given in SI units as described in the docstrings. ```{hint} In reality, `getB()` is proportional to the `polarization` input and therefore returns the same unit. For example, with polarization input in mT, `getB()` will return mT as well. At the same time when the `magnetization` input is in (kA/m), then `getH()` returns (kA/m) as well. The B/H-field outputs are related to a M/J-inputs via a factor of $µ_0$. ``` The output of a field computation `magpy.getB(sources, observers)` is by default a NumPy array of shape (s, p, o, o1, o2, ..., 3) where s is the number of input sources, p the (maximal) object path length, o the number of observers, o1, o2, ... the sensor pixel shape or the shape of the observer position array input and 3 the three magnetic field components $(B_x, B_y, B_z)$. * `squeeze`: If `True` (default) all axes of length 1 in the output (e.g. only a single source) are squeezed. * `pixel_agg`: Select a compatible NumPy aggregator function (e.g. `'min'`, `'mean'`) that is applied to the output. For example, with `pixel_agg='mean'` the mean field of all observer points is returned. With this option it is possible to supply `getBHJM` with multiple observers that have different pixel shapes. * `output`: Change the output format. Options are `'ndarray'` (default, returns a NumPy ndarray) and `'dataframe'` (returns a 2D-table Pandas DataFrame). ```{note} Magpylib collects all inputs (object parameters), and vectorizes them for the computation which reduces the computation time dramatically for large inputs. Try to make all field computations with as few calls to `getBHJM` as possible. Avoid Python loops at all costs! ``` ------------------------------------- (docs-field-functional)= ## Functional Interface Users can bypass the object-oriented interface and compute fields for i parameter sets via the functional interface—functions provided in the `magpylib.func` subpackage. These functions offer field computation, including source position and orientation. Inputs may be scalars or array-like of length i. Scalar inputs are automatically tiled to length i, creating i computation instances. The field is returned with shape (i, 3); when `squeeze=True` and `i=1`, the shape is (3,). For all functions, set `field` to `'B'` or `'H'`. The common defaults are `positions=(0, 0, 0)`, `orientations=None`, and `squeeze=True`. The following functions, with their specific parameters (see individual docstrings), make up the subpackage: - `circle_field(field, observers, diameters, currents)` - `polyline_field(field, observers, segments_start, segments_end, currents)` - `cuboid_field(field, observers, dimensions, polarizations)` - `cylinder_field(field, observers, dimensions, polarizations)` - `cylinder_segment_field(field, observers, dimensions, polarizations)` - `sphere_field(field, observers, diameters, polarizations)` - `tetrahedron_field(field, observers, vertices, polarizations)` - `dipole_field(field, observers, moments)` - `triangle_charge_field(field, observers, vertices, polarizations)` - `triangle_current_field(field, observers, vertices, current_densities)` The following code demonstrates the functional interface. ```python import numpy as np from magpylib.func import cuboid_field # All inputs and outputs in SI units # Compute the cuboid field for 3 input instances i = 3 # number of instances B = cuboid_field( field="B", observers=np.linspace((0, 0, 1), (0, 0, 3), i), dimensions=np.linspace((1, 1, 1), (3, 3, 3), 3, i), polarizations=(0, 0, 1), ) print(B.round(3)) # [[0. 0. 0.135] # [0. 0. 0.135] # [0. 0. 0.135]] ``` This example demonstrates the scale invariance ```{note} The functional interface is potentially faster than the object oriented one if users generate the input arrays efficiently with NumPy (e.g. `np.arange`, `np.linspace`, `np.tile`, `np.repeat`, ...). ``` ------------------------------------- (docs-field-core)= ## Core Interface At the heart of Magpylib lies a set of core functions that are our implementations of analytical field expressions found in the literature, see {ref}`guide-ressources-physics`. Direct access to these functions is given through the `magpylib.core` subpackage which includes, - `magnet_cuboid_Bfield(observers, dimensions, polarizations)` - `magnet_cylinder_axial_Bfield(z0, r, z)` - `magnet_cylinder_diametral_Hfield(z0, r, z, phi)` - `magnet_cylinder_segment_Hfield(observers, dimensions, magnetizations)` - `magnet_sphere_Bfield(observers, diameters, polarizations)` - `current_circle_Hfield(r0, r, z, i0)` - `current_polyline_Hfield(observers, segments_start, segments_end, currents)` - `current_sheet_Hfield(observers, vertices, current_densities)` - `dipole_Hfield(observers, moments)` - `triangle_Bfield(observers, vertices, polarizations)` All inputs must be NumPy ndarrays of shape (i, x). Details can be found in the respective function docstrings. The following example demonstrates the core interface. ```python import numpy as np import magpylib as magpy # All inputs and outputs in SI units # Prepare input z0 = np.array([1, 1]) r = np.array([1, 1]) z = np.array([2, 2]) # Compute field with core functions B = magpy.core.magnet_cylinder_axial_Bfield(z0=z0, r=r, z=z).T print(B) # --> [[0.05561469 0. 0.06690167] # [0.05561469 0. 0.06690167]] ``` ------------------------------------- ## Computation Workflow (B, H, J, M) The Magpylib field computation internal workflow and different approaches of the three interfaces is outlined in the following sketch. ```{figure} ../../../_static/images/docu_field_comp_flow.png :width: 100% :align: center :alt: Magpylib computational flow chart. Magpylib computational flow chart. ```