Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Alexboiboi
Copy link
Member

@Alexboiboi Alexboiboi commented Jan 10, 2024

Related Issues

Notes

This PR add the possibility to use inputs as units objects and also as outputs.

Units modes

Classes input parameters can be:

  • arrays or scalars
  • quantity object from a UnitHandler
  • unit-like inputs of the form (, ) (e.g. [[1,2,3], 'meter'])

When inputs are quantity objects, a dimensionality check is always performed. If it is only array-like or a scalar, it is assumed to be of base SI units. If it is unit-like it gets transformed into a quantity object. The following units_mode are implemented to cover a wide range of possible behaviors when dealing with units:

  • "consistent": either only units or none sould be used (first input determines the case).
  • "keep" : keep input object type. Allows and stores derived units.
  • "downcast" : allow unit-like inputs but converts to base SI units, stores the magnitude
    only.
  • "upcast" : converts to units quantity object if input is unit-like, keep
    otherwise.
  • "base" : converts to base SI units quantity object (e.g. 'mm' -> 'm').
  • "coerce": forces inputs to be unit-like (raise if not).
  • "forbid": forbids unit-like inputs.
from contextlib import contextmanager

import magpylib as magpy
from magpylib._src.defaults.defaults_utility import ALLOWED_UNITS_MODES

magpy.units.reset()
magpy.units.mode = "keep"
magpy.units.package = "pint"

Q_ = magpy.units.registry.Quantity

@contextmanager
def catch_raise(prefix="", max_len=200):
    try:
        yield
    except Exception as msg:
        s = str(msg).split("\n")[0][:max_len]
        print(f"{prefix}\033[91m raises: {s}\033[0m")


inputs_types = {
    "array-like": {
        "dimension": [0.001, 0.002, 0.003],
        "polarization": [0.001, 0.002, 0.003],
    },
    "unit-like": {
        "dimension": Q_([1,2,3], "mm"),
        "polarization": Q_([1,2,3], "mT"),
    },
    "mixed": {
        "dimension": Q_([1,2,3], "mm"),
        "polarization": [0.001, 0.002, 0.003],
    },
}
observers_types = {
    "array-like": [.001,.002,.003],
    "unit-like": Q_([1,2,3], "mm"),
    "mixed":[Q_([1,2,3], "mm"), [.001,.002,.003]],
}
for type_name, inputs in inputs_types.items():
    print(f"\n* inputs (\033[92m{type_name}\033[0m): {inputs}")
    for mode in ALLOWED_UNITS_MODES:
        magpy.units.reset()
        magpy.units.mode = mode
        print(f"\n -> units mode: \033[94m{mode!r}\033[0m")
        c = None
        # test inputs
        pref = "      stored: "
        with catch_raise(prefix=pref):
            c = magpy.magnet.Cuboid(**inputs)
            stored = {k: getattr(c, k) for k in inputs}
            print(f"{pref}{stored}")
        # test getB
        if False:#c is not None:
            for type_name, obs in observers_types.items():
                pref = f"      (\033[92m{type_name}\033[0m) getB({obs!r}): "
                with catch_raise(prefix=pref):
                    print(f"{pref}{c.getB(obs)!r}")

Gaussian units

This is a specific pint features that allows units-conversions with a context manager. For example the magnetization input requires A/m but with the Gaussian context it is allowed to be converted to equivalent T.

import magpylib as magpy

magpy.units.reset()
magpy.units.mode = "keep"
magpy.units.package = "pint"

c = magpy.magnet.Cuboid(dimension=[(1, 1, 1), "cm"], polarization=(0, 0, 1))
sens = magpy.Sensor(position=[(0, 0, 1), "cm"])

B1 = c.getB(sens)
c.polarization = [(0, 0, 1000), "mT"]

B2 = c.getB(sens)
with magpy.units.registry.context("Gaussian"):
    c.magnetization = [(0, 0, 1), "T"]  # instead of A/m, still works

B3 = c.getB(sens)
B1, B2, B3

Display

import magpylib as magpy
import numpy as np
import pyvista as pv

magpy.units.reset()
magpy.units.mode = "keep"
magpy.units.package = "pint"

objects = {
    "Cuboid": magpy.magnet.Cuboid(
        polarization=(0, -0.1, 0),
        dimension=[(1, 1, 1), "cm"],
        position=[(-60, 0, 0), "mm"],
    ),
    "Cylinder": magpy.magnet.Cylinder(
        polarization=[(0, 0, 10000), "µT"],
        dimension=[(10, 10), "mm"],
        position=[(-5, 0, 0), "cm"],
    ),
    "CylinderSegment": magpy.magnet.CylinderSegment(
        polarization=[(0, 0, 0.01), "T"],
        dimension=(0.003, 0.01, 0.01, 0, 140),
        position=(-0.03, 0, 0),
    ),
    "Sphere": magpy.magnet.Sphere(
        polarization=[(0, 0, 10), "mT"],
        diameter="0.01m",
        position=[(-1, 0, 0), "cm"],
    ),
    "Tetrahedron": magpy.magnet.Tetrahedron(
        polarization=[(0, 0, 10), "mT"],
        vertices=[((-1, 0, 0), (1, 0, 0), (0, -1, 0), (0, -1, -1)), "cm"],
        position=[(-4, 0, 4), "cm"],
    ),
    "TriangularMesh": magpy.magnet.TriangularMesh(
        polarization=[(0, 0, 10), "mT"],
        vertices=[pv.Dodecahedron(radius=1).triangulate().points, "cm"],
        faces=pv.Dodecahedron().triangulate().faces.reshape(-1, 4)[:, 1:],
        position=[(-0.01, 0, 0.04), "m"],
    ),
    "Circle": magpy.current.Circle(
        current="1A",
        diameter="0.01m",
        position=[(40, 0, 0), "mm"],
    ),
    "Polyline": magpy.current.Polyline(
        current="1000mA",
        vertices=[
            [
                (1, 0, 0),
                (0, 1, 0),
                (-1, 0, 0),
                (0, -1, 0),
                (1, 0, 0),
            ],
            "cm",
        ],
        position=[(1, 0, 0), "cm"],
    ),
    "Dipole": magpy.misc.Dipole(
        moment=[(0, 0, 1), "A*m**2"],
        position=[(0.03, 0, 0), "m"],
    ),
    "Triangle": magpy.misc.Triangle(
        polarization=[(0, 0, 10), "mT"],
        vertices=[((-0.01, 0, 0), (0.01, 0, 0), (0, 0.01, 0)), "m"],
        position=[(2, 0, 4), "cm"],
    ),
    "Sensor": magpy.Sensor(
        pixel=[[(0, 0, z) for z in (-5, 0, 5)], "mm"],
        position=[(0, -30, 0), "mm"],
    ),
}

objects["Circle"].move(np.linspace((0, 0, 0), (0, 0, 0.05), 20))
objects["Cuboid"].rotate_from_angax(np.linspace(0, 90, 20), "z", anchor=0)

magpy.show(*objects.values())

User-defined units handler

import magpylib as magpy

class MyUnitsHandler(magpy.units.UnitsHandler, package="package_name"):

    def is_quantity(self, inp):
        ...

    def to_quantity(self, inp, unit):
        ...

    def to_unit(self, inp, unit):
        ...

    def get_unit(self, inp):
        ...

    def get_magnitude(self, inp):
        ...
   
mapgy.units.package="package_name"     

OrtnerMichael and others added 30 commits December 26, 2023 00:49
@OrtnerMichael OrtnerMichael modified the milestones: Version 5, Version 5.1 Mar 12, 2024
@Alexboiboi Alexboiboi self-assigned this Jun 4, 2024
@Alexboiboi Alexboiboi modified the milestones: Version 5.1, Future Mar 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Make the library compatible with unit packages like pint or unyt
2 participants