diff --git a/README.md b/README.md index 60d05e32..6084a9cb 100644 --- a/README.md +++ b/README.md @@ -10,20 +10,16 @@ The core of Traceon is completely free to use and open source. There is a commer [Examples](https://github.com/leon-vv/Traceon/tree/main/examples) -[API documentation v0.6.0](https://traceon.org/docs/v0.6.0/index.html) - -[API documentation v0.5.0](https://traceon.org/docs/v0.5.0/index.html) +[API documentation v0.7.0](https://traceon.org/docs/v0.7.0/index.html) -[API documentation v0.4.0](https://traceon.org/docs/v0.4.0/index.html) - -[API documentation v0.3.1](https://traceon.org/docs/v0.3.1/index.html) +[API documentation v0.6.0](https://traceon.org/docs/v0.6.0/index.html) ## Citation Please cite the software as follows: ``` -L.B. van Velzen. Traceon software (version 0.6.0). 2023. https://doi.org/10.5281/zenodo.11528404 +L.B. van Velzen. Traceon software (version 0.7.0). 2024. https://doi.org/10.5281/zenodo.14176070 ``` ## Installation diff --git a/docs/docs/v0.7.0rc2/excitation.html b/docs/docs/v0.7.0/excitation.html similarity index 100% rename from docs/docs/v0.7.0rc2/excitation.html rename to docs/docs/v0.7.0/excitation.html diff --git a/docs/docs/v0.7.0rc2/focus.html b/docs/docs/v0.7.0/focus.html similarity index 100% rename from docs/docs/v0.7.0rc2/focus.html rename to docs/docs/v0.7.0/focus.html diff --git a/docs/docs/v0.7.0rc3/geometry.html b/docs/docs/v0.7.0/geometry.html similarity index 100% rename from docs/docs/v0.7.0rc3/geometry.html rename to docs/docs/v0.7.0/geometry.html diff --git a/docs/docs/v0.7.0rc3/index.html b/docs/docs/v0.7.0/index.html similarity index 99% rename from docs/docs/v0.7.0rc3/index.html rename to docs/docs/v0.7.0/index.html index 60dab098..cb9b6e96 100644 --- a/docs/docs/v0.7.0rc3/index.html +++ b/docs/docs/v0.7.0/index.html @@ -33,7 +33,7 @@

Package traceon

Electron tracing can be done very quickly using accurate radial series interpolation in both geometries. The electron trajectories obtained can help determine the aberrations of the optical components under study.

If you have any issues using the package, please open an issue on the Traceon Github page.

-

The software is currently distributed under the AGPLv3 license.

+

The software is currently distributed under the MPL 2.0 license.

Usage

In general, one starts with the traceon.geometry module to create a mesh. For the BEM only the boundary of electrodes needs to be meshed. So in 2D (radial symmetry) the mesh consists of line elements while in 3D the diff --git a/docs/docs/v0.7.0rc2/interpolation.html b/docs/docs/v0.7.0/interpolation.html similarity index 100% rename from docs/docs/v0.7.0rc2/interpolation.html rename to docs/docs/v0.7.0/interpolation.html diff --git a/docs/docs/v0.7.0rc1/logging.html b/docs/docs/v0.7.0/logging.html similarity index 100% rename from docs/docs/v0.7.0rc1/logging.html rename to docs/docs/v0.7.0/logging.html diff --git a/docs/docs/v0.7.0rc3/mesher.html b/docs/docs/v0.7.0/mesher.html similarity index 100% rename from docs/docs/v0.7.0rc3/mesher.html rename to docs/docs/v0.7.0/mesher.html diff --git a/docs/docs/v0.7.0rc1/plotting.html b/docs/docs/v0.7.0/plotting.html similarity index 100% rename from docs/docs/v0.7.0rc1/plotting.html rename to docs/docs/v0.7.0/plotting.html diff --git a/docs/docs/v0.7.0rc2/solver.html b/docs/docs/v0.7.0/solver.html similarity index 100% rename from docs/docs/v0.7.0rc2/solver.html rename to docs/docs/v0.7.0/solver.html diff --git a/docs/docs/v0.7.0rc2/tracing.html b/docs/docs/v0.7.0/tracing.html similarity index 100% rename from docs/docs/v0.7.0rc2/tracing.html rename to docs/docs/v0.7.0/tracing.html diff --git a/docs/docs/v0.7.0rc1/excitation.html b/docs/docs/v0.7.0rc1/excitation.html deleted file mode 100644 index 6f37fe19..00000000 --- a/docs/docs/v0.7.0rc1/excitation.html +++ /dev/null @@ -1,706 +0,0 @@ - - - - - - -traceon.excitation API documentation - - - - - - - - - - - - - -

-
-
-

Module traceon.excitation

-
-
-

The excitation module allows to specify the excitation (or element types) of the different physical groups (electrodes) -created with the traceon.geometry module.

-

The possible excitations are as follows:

-
    -
  • Fixed voltage (electrode connect to a power supply)
  • -
  • Voltage function (a generic Python function specifies the voltage as a function of position)
  • -
  • Dielectric, with arbitrary electric permittivity
  • -
  • Current coil, with fixed total amount of current (only in radial symmetry)
  • -
  • Magnetostatic scalar potential
  • -
  • Magnetizable material, with arbitrary magnetic permeability
  • -
-

Currently current excitations are not supported in 3D. But magnetostatic fields can still be computed using the magnetostatic scalar potential.

-

Once the excitation is specified, it can be passed to solve_direct() to compute the resulting field.

-
-
-
-
-
-
-
-
-

Classes

-
-
-class Excitation -(mesh, symmetry) -
-
-
-
- -Expand source code - -
class Excitation:
-    """ """
-     
-    def __init__(self, mesh, symmetry):
-        self.mesh = mesh
-        self.electrodes = mesh.get_electrodes()
-        self.excitation_types = {}
-        self.symmetry = symmetry
-         
-        if symmetry == Symmetry.RADIAL:
-            assert self.mesh.points.shape[1] == 2 or np.all(self.mesh.points[:, 1] == 0.), \
-                "When symmetry is RADIAL, the geometry should lie in the XZ plane"
-    
-    def __str__(self):
-        return f'<Traceon Excitation,\n\t' \
-            + '\n\t'.join([f'{n}={v} ({t})' for n, (t, v) in self.excitation_types.items()]) \
-            + '>'
-     
-    def add_voltage(self, **kwargs):
-        """
-        Apply a fixed voltage to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the voltages in units of Volt. For example,
-            calling the function as `add_voltage(lens=50)` assigns a 50V value to the geometry elements part of the 'lens' physical group.
-            Alternatively, the value can be a function, which takes x, y, z coordinates as argument and returns the voltage at that position.
-            Note that in 2D symmetries (such as radial symmetry) the z value for this function will always be zero.
-        
-        """
-        for name, voltage in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            if isinstance(voltage, int) or isinstance(voltage, float):
-                self.excitation_types[name] = (ExcitationType.VOLTAGE_FIXED, voltage)
-            elif callable(voltage):
-                self.excitation_types[name] = (ExcitationType.VOLTAGE_FUN, voltage)
-            else:
-                raise NotImplementedError('Unrecognized voltage value')
-
-    def add_current(self, **kwargs):
-        """
-        Apply a fixed total current to the geometries assigned the given name. Note that a coil is assumed,
-        which implies that the current density is constant as a function of (r, z). In a solid piece of conducting material the current density would
-        be higher at small r (as the 'loop' around the axis is shorter and therefore the resistance is lower).
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the currents in units of Ampere. For example,
-            calling the function as `add_current(coild=10)` assigns a 10A value to the geometry elements part of the 'coil' physical group.
-        """
-
-        assert self.symmetry == Symmetry.RADIAL, "Currently magnetostatics are only supported for radially symmetric meshes"
-         
-        for name, current in kwargs.items():
-            assert name in self.mesh.physical_to_triangles.keys(), "Current can only be applied to a triangle electrode"
-            self.excitation_types[name] = (ExcitationType.CURRENT, current)
-
-    def has_current(self):
-        """Check whether a current is applied in this excitation."""
-        return any([t == ExcitationType.CURRENT for t, _ in self.excitation_types.values()])
-    
-    def is_electrostatic(self):
-        """Check whether the excitation contains electrostatic fields."""
-        return any([t in [ExcitationType.VOLTAGE_FIXED, ExcitationType.VOLTAGE_FUN] for t, _ in self.excitation_types.values()])
-     
-    def is_magnetostatic(self):
-        """Check whether the excitation contains magnetostatic fields."""
-        return any([t in [ExcitationType.MAGNETOSTATIC_POT, ExcitationType.CURRENT] for t, _ in self.excitation_types.values()])
-     
-    def add_magnetostatic_potential(self, **kwargs):
-        """
-        Apply a fixed magnetostatic potential to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the voltages in units of Ampere. For example,
-            calling the function as `add_magnetostatic_potential(lens=50)` assigns a 50A value to the geometry elements part of the 'lens' physical group.
-        """
-        for name, pot in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.MAGNETOSTATIC_POT, pot)
-
-    def add_magnetizable(self, **kwargs):
-        """
-        Assign a relative magnetic permeability to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example,
-            calling the function as `add_dielectric(spacer=2)` assign the relative dielectric constant of 2 to the `spacer` physical group.
-         
-        """
-
-        for name, permeability in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.MAGNETIZABLE, permeability)
-     
-    def add_dielectric(self, **kwargs):
-        """
-        Assign a dielectric constant to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example,
-            calling the function as `add_dielectric(spacer=2)` assign the relative dielectric constant of 2 to the `spacer` physical group.
-         
-        """
-        for name, permittivity in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.DIELECTRIC, permittivity)
-
-    def add_electrostatic_boundary(self, *args):
-        """
-        Specify geometry elements as electrostatic boundary elements. At the boundary we require E·n = 0 at every point on the boundary. This
-        is equivalent to stating that the directional derivative of the electrostatic potential through the boundary is zero. Placing boundaries between
-        the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a dielectric with a dielectric
-        constant of zero. This is how a boundary is actually implemented internally.
-        
-        Parameters
-        ----------
-        *args: list of str
-            The geometry names that should be considered a boundary.
-        """
-        self.add_dielectric(**{a:0 for a in args})
-    
-    def add_magnetostatic_boundary(self, *args):
-        """
-        Specify geometry elements as magnetostatic boundary elements. At the boundary we require H·n = 0 at every point on the boundary. This
-        is equivalent to stating that the directional derivative of the magnetostatic potential through the boundary is zero. Placing boundaries between
-        the spaces of electrodes usually helps convergence tremendously. 
-        
-        Parameters
-        ----------
-        *args: list of str
-            The geometry names that should be considered a boundary.
-        """
-
-        self.add_magnetizable(**{a:0 for a in args})
-    
-    def _split_for_superposition(self):
-        
-        # Names that have a fixed voltage excitation, not equal to 0.0
-        types = self.excitation_types
-        non_zero_fixed = [n for n, (t, v) in types.items() if t in [ExcitationType.VOLTAGE_FIXED,
-                                                                    ExcitationType.CURRENT] and v != 0.0]
-        
-        excitations = []
-         
-        for name in non_zero_fixed:
-             
-            new_types_dict = {}
-             
-            for n, (t, v) in types.items():
-                assert t != ExcitationType.VOLTAGE_FUN, "VOLTAGE_FUN excitation not supported for superposition."
-                 
-                if n == name:
-                    new_types_dict[n] = (t, 1.0)
-                elif t == ExcitationType.VOLTAGE_FIXED:
-                    new_types_dict[n] = (t, 0.0)
-                elif t == ExcitationType.CURRENT:
-                    new_types_dict[n] = (t, 0.0)
-                else:
-                    new_types_dict[n] = (t, v)
-            
-            exc = Excitation(self.mesh)
-            exc.excitation_types = new_types_dict
-            excitations.append(exc)
-
-        assert len(non_zero_fixed) == len(excitations)
-        return {n:e for (n,e) in zip(non_zero_fixed, excitations)}
-
-    def _get_active_elements(self, type_):
-        assert type_ in ['electrostatic', 'magnetostatic']
-        
-        if self.symmetry == Symmetry.RADIAL:
-            elements = self.mesh.lines
-            physicals = self.mesh.physical_to_lines
-        else:
-            elements = self.mesh.triangles
-            physicals = self.mesh.physical_to_triangles
-
-        def type_check(excitation_type):
-            if type_ == 'electrostatic':
-                return excitation_type.is_electrostatic()
-            else:
-                return excitation_type in [ExcitationType.MAGNETIZABLE, ExcitationType.MAGNETOSTATIC_POT]
-        
-        inactive = np.full(len(elements), True)
-        for name, value in self.excitation_types.items():
-            if type_check(value[0]):
-                inactive[ physicals[name] ] = False
-         
-        map_index = np.arange(len(elements)) - np.cumsum(inactive)
-        names = {n:map_index[i] for n, i in physicals.items() \
-                    if n in self.excitation_types and type_check(self.excitation_types[n][0])}
-         
-        return self.mesh.points[ elements[~inactive] ], names
-     
-    def get_electrostatic_active_elements(self):
-        """Get elements in the mesh that have an electrostatic excitation
-        applied to them. 
-         
-        Returns
-        --------
-        A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. \
-        This array contains the vertices of the line elements or the triangles. \
-        Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line \
-        element is given by a polynomial interpolation of the points. \
-        names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, \
-        while the values are Numpy arrays of indices that can be used to index the points array.
-        """
-        return self._get_active_elements('electrostatic')
-    
-    def get_magnetostatic_active_elements(self):
-        """Get elements in the mesh that have an magnetostatic excitation
-        applied to them. This does not include current excitation, as these are not part of the matrix.
-    
-        Returns
-        --------
-        A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. \
-        This array contains the vertices of the line elements or the triangles. \
-        Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line \
-        element is given by a polynomial interpolation of the points. \
-        names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, \
-        while the values are Numpy arrays of indices that can be used to index the points array.
-        """
-
-        return self._get_active_elements('magnetostatic')
-    
-    def get_number_of_electrostatic_active_elements(self):
-        """Get elements in the mesh that are active, in the sense that
-        an excitation to them has been applied. This is the length of the points
-        array returned by the `Excitation.get_electrostatic_active_elements`.
-
-        Returns
-        --------
-        int, giving the number of elements. """
-        return self._get_number_of_active_elements('electrostatic')
-    
-    def get_number_of_electrostatic_matrix_elements(self):
-        """Gets the number of elements along one axis of the matrix. If this function returns N, the
-        matrix will have size NxN. The matrix consists of 64bit float values. Therefore the size of the matrix
-        in bytes is 8·NxN.
-
-        Returns
-        ---------
-        integer number
-        """
-        return self._get_number_of_active_elements('electrostatic')
-
-

Methods

-
-
-def add_current(self, **kwargs) -
-
-

Apply a fixed total current to the geometries assigned the given name. Note that a coil is assumed, -which implies that the current density is constant as a function of (r, z). In a solid piece of conducting material the current density would -be higher at small r (as the 'loop' around the axis is shorter and therefore the resistance is lower).

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the currents in units of Ampere. For example, -calling the function as add_current(coild=10) assigns a 10A value to the geometry elements part of the 'coil' physical group.
-
-
-
-def add_dielectric(self, **kwargs) -
-
-

Assign a dielectric constant to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example, -calling the function as add_dielectric(spacer=2) assign the relative dielectric constant of 2 to the spacer physical group.
-
-
-
-def add_electrostatic_boundary(self, *args) -
-
-

Specify geometry elements as electrostatic boundary elements. At the boundary we require E·n = 0 at every point on the boundary. This -is equivalent to stating that the directional derivative of the electrostatic potential through the boundary is zero. Placing boundaries between -the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a dielectric with a dielectric -constant of zero. This is how a boundary is actually implemented internally.

-

Parameters

-
-
*args : list of str
-
The geometry names that should be considered a boundary.
-
-
-
-def add_magnetizable(self, **kwargs) -
-
-

Assign a relative magnetic permeability to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example, -calling the function as add_dielectric(spacer=2) assign the relative dielectric constant of 2 to the spacer physical group.
-
-
-
-def add_magnetostatic_boundary(self, *args) -
-
-

Specify geometry elements as magnetostatic boundary elements. At the boundary we require H·n = 0 at every point on the boundary. This -is equivalent to stating that the directional derivative of the magnetostatic potential through the boundary is zero. Placing boundaries between -the spaces of electrodes usually helps convergence tremendously.

-

Parameters

-
-
*args : list of str
-
The geometry names that should be considered a boundary.
-
-
-
-def add_magnetostatic_potential(self, **kwargs) -
-
-

Apply a fixed magnetostatic potential to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the voltages in units of Ampere. For example, -calling the function as add_magnetostatic_potential(lens=50) assigns a 50A value to the geometry elements part of the 'lens' physical group.
-
-
-
-def add_voltage(self, **kwargs) -
-
-

Apply a fixed voltage to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the voltages in units of Volt. For example, -calling the function as add_voltage(lens=50) assigns a 50V value to the geometry elements part of the 'lens' physical group. -Alternatively, the value can be a function, which takes x, y, z coordinates as argument and returns the voltage at that position. -Note that in 2D symmetries (such as radial symmetry) the z value for this function will always be zero.
-
-
-
-def get_electrostatic_active_elements(self) -
-
-

Get elements in the mesh that have an electrostatic excitation -applied to them.

-

Returns

-

A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. -This array contains the vertices of the line elements or the triangles. -Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line -element is given by a polynomial interpolation of the points. -names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, -while the values are Numpy arrays of indices that can be used to index the points array.

-
-
-def get_magnetostatic_active_elements(self) -
-
-

Get elements in the mesh that have an magnetostatic excitation -applied to them. This does not include current excitation, as these are not part of the matrix.

-

Returns

-

A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. -This array contains the vertices of the line elements or the triangles. -Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line -element is given by a polynomial interpolation of the points. -names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, -while the values are Numpy arrays of indices that can be used to index the points array.

-
-
-def get_number_of_electrostatic_active_elements(self) -
-
-

Get elements in the mesh that are active, in the sense that -an excitation to them has been applied. This is the length of the points -array returned by the Excitation.get_electrostatic_active_elements().

-

Returns

-

int, giving the number of elements.

-
-
-def get_number_of_electrostatic_matrix_elements(self) -
-
-

Gets the number of elements along one axis of the matrix. If this function returns N, the -matrix will have size NxN. The matrix consists of 64bit float values. Therefore the size of the matrix -in bytes is 8·NxN.

-

Returns

-
-
integer number
-
 
-
-
-
-def has_current(self) -
-
-

Check whether a current is applied in this excitation.

-
-
-def is_electrostatic(self) -
-
-

Check whether the excitation contains electrostatic fields.

-
-
-def is_magnetostatic(self) -
-
-

Check whether the excitation contains magnetostatic fields.

-
-
-
-
-class ExcitationType -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Possible excitation that can be applied to elements of the geometry. See the methods of Excitation for documentation.

-
- -Expand source code - -
class ExcitationType(IntEnum):
-    """Possible excitation that can be applied to elements of the geometry. See the methods of `Excitation` for documentation."""
-    VOLTAGE_FIXED = 1
-    VOLTAGE_FUN = 2
-    DIELECTRIC = 3
-     
-    CURRENT = 4
-    MAGNETOSTATIC_POT = 5
-    MAGNETIZABLE = 6
-     
-    def is_electrostatic(self):
-        return self in [ExcitationType.VOLTAGE_FIXED,
-                        ExcitationType.VOLTAGE_FUN,
-                        ExcitationType.DIELECTRIC]
-
-    def is_magnetostatic(self):
-        return self in [ExcitationType.MAGNETOSTATIC_POT,
-                        ExcitationType.MAGNETIZABLE,
-                        ExcitationType.CURRENT]
-     
-    def __str__(self):
-        if self == ExcitationType.VOLTAGE_FIXED:
-            return 'voltage fixed'
-        elif self == ExcitationType.VOLTAGE_FUN:
-            return 'voltage function'
-        elif self == ExcitationType.DIELECTRIC:
-            return 'dielectric'
-        elif self == ExcitationType.CURRENT:
-            return 'current'
-        elif self == ExcitationType.MAGNETOSTATIC_POT:
-            return 'magnetostatic potential'
-        elif self == ExcitationType.MAGNETIZABLE:
-            return 'magnetizable'
-         
-        raise RuntimeError('ExcitationType not understood in __str__ method')
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var CURRENT
-
-
-
-
var DIELECTRIC
-
-
-
-
var MAGNETIZABLE
-
-
-
-
var MAGNETOSTATIC_POT
-
-
-
-
var VOLTAGE_FIXED
-
-
-
-
var VOLTAGE_FUN
-
-
-
-
-

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-
-
-class Symmetry -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Symmetry to be used for solver. Used when deciding which formulas to use in the Boundary Element Method. The currently -supported symmetries are radial symmetry (also called cylindrical symmetry) and general 3D geometries.

-
- -Expand source code - -
class Symmetry(IntEnum):
-    """Symmetry to be used for solver. Used when deciding which formulas to use in the Boundary Element Method. The currently
-    supported symmetries are radial symmetry (also called cylindrical symmetry) and general 3D geometries.
-    """
-    RADIAL = 0
-    THREE_D = 2
-    
-    def __str__(self):
-        if self == Symmetry.RADIAL:
-            return 'radial'
-        elif self == Symmetry.THREE_D:
-            return '3d' 
-    
-    def is_2d(self):
-        return self == Symmetry.RADIAL
-        
-    def is_3d(self):
-        return self == Symmetry.THREE_D
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var RADIAL
-
-
-
-
var THREE_D
-
-
-
-
-

Methods

-
-
-def is_2d(self) -
-
-
-
-
-def is_3d(self) -
-
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/focus.html b/docs/docs/v0.7.0rc1/focus.html deleted file mode 100644 index 71ba777b..00000000 --- a/docs/docs/v0.7.0rc1/focus.html +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - -traceon.focus API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.focus

-
-
-

Module containing a single function to find the focus of a beam of electron trajecories.

-
-
-
-
-
-
-

Functions

-
-
-def focus_position(positions) -
-
-

Find the focus of the given trajectories (which are returned from Tracer.__call__()). -The focus is found using a least square method by considering the final positions and velocities of -the given trajectories and linearly extending the trajectories backwards.

-

Parameters

-
-
positions : iterable of (N,4) or (N,6) np.ndarray float64
-
Trajectories of electrons, as returned by Tracer.__call__()
-
-

Returns

-

A tuple of size two or three, depending on whether the input positions are 2D or 3D trajectories. The -returned position is the (r, z) or (x, y, z) position of the focus.

-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/geometry.html b/docs/docs/v0.7.0rc1/geometry.html deleted file mode 100644 index 9c776fe0..00000000 --- a/docs/docs/v0.7.0rc1/geometry.html +++ /dev/null @@ -1,2012 +0,0 @@ - - - - - - -traceon.geometry API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.geometry

-
-
-

The geometry module allows the creation of general meshes in 2D and 3D. -The builtin mesher uses so called parametric meshes, meaning -that for any mesh we construct a mathematical formula mapping to points on the mesh. This makes it -easy to generate structured (or transfinite) meshes. These meshes usually help the mesh to converge -to the right answer faster, since the symmetries of the mesh (radial, multipole, etc.) are better -represented.

-

The parametric mesher also has downsides, since it's for example harder to generate meshes with -lots of holes in them (the 'cut' operation is not supported). For these cases, Traceon makes it easy to import -meshes generated by other programs (e.g. GMSH or Comsol). Traceon can import meshio meshes -or any file format supported by meshio.

-
-
-
-
-
-
-
-
-

Classes

-
-
-class Path -(fun, path_length, breakpoints=[], name=None) -
-
-

A path is a mapping from a number in the range [0, path_length] to a three dimensional point. Note that Path is a -subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Path(GeometricObject):
-    """A path is a mapping from a number in the range [0, path_length] to a three dimensional point. Note that `Path` is a
-    subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-    
-    def __init__(self, fun, path_length, breakpoints=[], name=None):
-        # Assumption: fun takes in p, the path length
-        # and returns the point on the path
-        self.fun = fun
-        self.path_length = path_length
-        assert self.path_length > 0
-        self.breakpoints = breakpoints
-        self.name = name
-    
-    def from_irregular_function(to_point, N=100, breakpoints=[]):
-        """Construct a path from a function that is of the form u -> point, where 0 <= u <= 1.
-        The length of the path is determined by integration.
-
-        Parameters
-        ---------------------------------
-        to_point: callable
-            A function accepting a number in the range [0, 1] and returns a the dimensional point.
-        N: int
-            Number of samples to use in the cubic spline interpolation.
-        breakpoints: float iterable
-            Points (0 <= u <= 1) on the path where the function is non-differentiable. These points
-            are always included in the resulting mesh.
-
-        Returns
-        ---------------------------------
-        Path"""
-         
-        # path length = integrate |f'(x)|
-        fun = lambda u: np.array(to_point(u))
-        
-        u = np.linspace(0, 1, N)
-        samples = CubicSpline(u, [fun(u_) for u_ in u])
-        derivatives = samples.derivative()(u)
-        norm_derivatives = np.linalg.norm(derivatives, axis=1)
-        path_lengths = CubicSpline(u, norm_derivatives).antiderivative()(u)
-        interpolation = CubicSpline(path_lengths, u) # Path length to [0,1]
-        
-        return Path(lambda pl: fun(interpolation(pl)), path_lengths[-1], breakpoints=[b*path_length for b in breakpoints])
-    
-    def spline_through_points(points, N=100):
-        """Construct a path by fitting a cubic spline through the given points.
-
-        Parameters
-        -------------------------
-        points: (N, 3) ndarray of float
-            Three dimensional points through which the spline is fitted.
-
-        Returns
-        -------------------------
-        Path"""
-
-        x = np.linspace(0, 1, len(points))
-        interp = CubicSpline(x, points)
-        return Path.from_irregular_function(interp, N=N)
-     
-    def average(self, fun):
-        """Average a function along the path, by integrating 1/l * fun(path(l)) with 0 <= l <= path length.
-
-        Parameters
-        --------------------------
-        fun: callable (3,) -> float
-            A function taking a three dimensional point and returning a float.
-
-        Returns
-        -------------------------
-        float
-
-        The average value of the function along the point."""
-        return quad(lambda s: fun(self(s)), 0, self.path_length, points=self.breakpoints)[0]/self.path_length
-     
-    def map_points(self, fun):
-        """Return a new function by mapping a function over points along the path (see `traceon.mesher.GeometricObject`).
-        The path length is assumed to stay the same after this operation.
-        
-        Parameters
-        ----------------------------
-        fun: callable (3,) -> (3,)
-            Function taking three dimensional points and returning three dimensional points.
-
-        Returns
-        ---------------------------
-        Path"""
-        return Path(lambda u: fun(self(u)), self.path_length, self.breakpoints, name=self.name)
-     
-    def __call__(self, t):
-        """Evaluate a point along the path.
-
-        Parameters
-        ------------------------
-        t: float
-            The length along the path.
-
-        Returns
-        ------------------------
-        (3,) float
-
-        Three dimensional point."""
-        return self.fun(t)
-     
-    def is_closed(self):
-        """Determine whether the path is closed, by comparing the starting and endpoint.
-
-        Returns
-        ----------------------
-        bool: True if the path is closed, False otherwise."""
-        return _points_close(self.starting_point(), self.endpoint())
-    
-    def add_phase(self, l):
-        """Add a phase to a closed path. A path is closed when the starting point is equal to the
-        end point. A phase of length l means that the path starts 'further down' the closed path.
-
-        Parameters
-        --------------------
-        l: float
-            The phase (expressed as a path length). The resulting path starts l distance along the 
-            original path.
-
-        Returns
-        --------------------
-        Path"""
-        assert self.is_closed()
-        
-        def fun(u):
-            return self( (l + u) % self.path_length )
-        
-        return Path(fun, self.path_length, sorted([(b-l)%self.path_length for b in self.breakpoints + [0.]]), name=self.name)
-     
-    def __rshift__(self, other):
-        """Combine two paths to create a single path. The endpoint of the first path needs
-        to match the starting point of the second path. This common point is marked as a breakpoint and
-        always included in the mesh. To use this function use the right shift operator (p1 >> p2).
-
-        Parameters
-        -----------------------
-        other: Path
-            The second path, to extend the current path.
-
-        Returns
-        -----------------------
-        Path"""
-
-        assert isinstance(other, Path), "Exteding path with object that is not actually a Path"
-
-        assert _points_close(self.endpoint(), other.starting_point())
-
-        total = self.path_length + other.path_length
-         
-        def f(t):
-            assert 0 <= t <= total
-            
-            if t <= self.path_length:
-                return self(t)
-            else:
-                return other(t - self.path_length)
-        
-        return Path(f, total, self.breakpoints + [self.path_length] + other.breakpoints, name=self.name)
-
-    def starting_point(self):
-        """Returns the starting point of the path.
-
-        Returns
-        ---------------------
-        (3,) float
-
-        The starting point of the path."""
-        return self(0.)
-    
-    def middle_point(self):
-        """Returns the midpoint of the path (in terms of length along the path.)
-
-        Returns
-        ----------------------
-        (3,) float
-        
-        The point at the middle of the path."""
-        return self(self.path_length/2)
-    
-    def endpoint(self):
-        """Returns the endpoint of the path.
-
-        Returns
-        ------------------------
-        (3,) float
-        
-        The endpoint of the path."""
-        return self(self.path_length)
-    
-    def line_to(self, point):
-        """Extend the current path by a line from the current endpoint to the given point.
-        The given point is marked a breakpoint.
-
-        Parameters
-        ----------------------
-        point: (3,) float
-            The new endpoint.
-
-        Returns
-        ---------------------
-        Path"""
-        point = np.array(point)
-        assert point.shape == (3,), "Please supply a three dimensional point to .line_to(...)"
-        l = Path.line(self.endpoint(), point)
-        return self >> l
-     
-    def circle_xz(x0, z0, radius, angle=2*pi):
-        """Returns (part of) a circle in the XZ plane around the x-axis. Starting on the positive x-axis.
-        
-        Parameters
-        --------------------------------
-        x0: float
-            x-coordinate of the center of the circle
-        z0: float
-            z-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([radius*cos(theta), 0., radius*sin(theta)])
-        return Path(f, angle*radius).move(dx=x0, dz=z0)
-    
-    def circle_yz(y0, z0, radius, angle=2*pi):
-        """Returns (part of) a circle in the YZ plane around the x-axis. Starting on the positive y-axis.
-        
-        Parameters
-        --------------------------------
-        y0: float
-            x-coordinate of the center of the circle
-        z0: float
-            z-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([0., radius*cos(theta), radius*sin(theta)])
-        return Path(f, angle*radius).move(dy=y0, dz=z0)
-    
-    def circle_xy(x0, y0, radius, angle=2*pi):
-        """Returns (part of) a circle in the XY plane around the z-axis. Starting on the positive X-axis.
-        
-        Parameters
-        --------------------------------
-        y0: float
-            x-coordinate of the center of the circle
-        y0: float
-            y-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([radius*cos(theta), radius*sin(theta), 0.])
-        return Path(f, angle*radius).move(dx=x0, dy=y0)
-     
-    def arc_to(self, center, end, reverse=False):
-        """Extend the current path using an arc.
-
-        Parameters
-        ----------------------------
-        center: (3,) float
-            The center point of the arc.
-        end: (3,) float
-            The endpoint of the arc, shoud lie on a circle determined
-            by the given centerpoint and the current endpoint.
-
-        Returns
-        -----------------------------
-        Path"""
-        start = self.endpoint()
-        return self >> Path.arc(center, start, end, reverse=reverse)
-    
-    def arc(center, start, end, reverse=False):
-        """Return an arc by specifying the center, start and end point.
-
-        Parameters
-        ----------------------------
-        center: (3,) float
-            The center point of the arc.
-        start: (3,) float
-            The start point of the arc.
-        end: (3,) float
-            The endpoint of the arc.
-
-        Returns
-        ----------------------------
-        Path"""
-        start, center, end = np.array(start), np.array(center), np.array(end)
-         
-        x_unit = start - center
-        x_unit /= np.linalg.norm(x_unit)
-
-        vector = end - center
-         
-        y_unit = vector - np.dot(vector, x_unit) * x_unit
-        y_unit /= np.linalg.norm(y_unit)
-
-        radius = np.linalg.norm(start - center) 
-        theta_max = atan2(np.dot(vector, y_unit), np.dot(vector, x_unit))
-
-        if reverse:
-            theta_max = theta_max - 2*pi
-
-        path_length = abs(theta_max * radius)
-          
-        def f(l):
-            theta = l/path_length * theta_max
-            return center + radius*cos(theta)*x_unit + radius*sin(theta)*y_unit
-        
-        return Path(f, path_length)
-     
-    def revolve_x(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the x-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-        
-        pstart, pmiddle, pend = self.starting_point(), self.middle_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[1]**2 + p[2]**2))
-        length2 = 2*pi*r_avg
-         
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[2], p[1])
-            r = sqrt(p[1]**2 + p[2]**2)
-            return np.array([p[0], r*cos(theta + v/length2*angle), r*sin(theta + v/length2*angle)])
-         
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-    
-    def revolve_y(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the y-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-
-        pstart, pend = self.starting_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[0]**2 + p[2]**2))
-        length2 = 2*pi*r_avg
-         
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[2], p[0])
-            r = sqrt(p[0]*p[0] + p[2]*p[2])
-            return np.array([r*cos(theta + v/length2*angle), p[1], r*sin(theta + v/length2*angle)])
-         
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-    
-    def revolve_z(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the z-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-
-        pstart, pend = self.starting_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[0]**2 + p[1]**2))
-        length2 = 2*pi*r_avg
-        
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[1], p[0])
-            r = sqrt(p[0]*p[0] + p[1]*p[1])
-            return np.array([r*cos(theta + v/length2*angle), r*sin(theta + v/length2*angle), p[2]])
-        
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-     
-    def extrude(self, vector):
-        """Create a surface by extruding the path along a vector. The vector gives both
-        the length and the direction of the extrusion.
-
-        Parameters
-        -------------------------
-        vector: (3,) float
-            The direction and length (norm of the vector) to extrude by.
-
-        Returns
-        -------------------------
-        Surface"""
-        vector = np.array(vector)
-        length = np.linalg.norm(vector)
-         
-        def f(u, v):
-            return self(u) + v/length*vector
-        
-        return Surface(f, self.path_length, length, self.breakpoints, name=self.name)
-    
-    def extrude_by_path(self, p2):
-        """Create a surface by extruding the path along a second path. The second
-        path does not need to start along the first path. Imagine the surface created
-        by moving the first path along the second path.
-
-        Parameters
-        -------------------------
-        p2: Path
-            The (second) path defining the extrusion.
-
-        Returns
-        ------------------------
-        Surface"""
-        p0 = p2.starting_point()
-         
-        def f(u, v):
-            return self(u) + p2(v) - p0
-
-        return Surface(f, self.path_length, p2.path_length, self.breakpoints, p2.breakpoints, name=self.name)
-
-    def close(self):
-        """Close the path, by making a straight line to the starting point.
-
-        Returns
-        -------------------
-        Path"""
-        return self.line_to(self.starting_point())
-    
-    def ellipse(major, minor):
-        """Create a path along the outline of an ellipse. The ellipse lies
-        in the XY plane, and the path starts on the positive x-axis.
-
-        Parameters
-        ---------------------------
-        major: float
-            The major axis of the ellipse (lies along the x-axis).
-        minor: float
-            The minor axis of the ellipse (lies along the y-axis).
-
-        Returns
-        ---------------------------
-        Path"""
-        # Crazy enough there is no closed formula
-        # to go from path length to a point on the ellipse.
-        # So we have to use `from_irregular_function`
-        def f(u):
-            return np.array([major*cos(2*pi*u), minor*sin(2*pi*u), 0.])
-        return Path.from_irregular_function(f)
-    
-    def line(from_, to):
-        """Create a straight line between two points.
-
-        Parameters
-        ------------------------------
-        from_: (3,) float
-            The starting point of the path.
-        to: (3,) float
-            The endpoint of the path.
-
-        Returns
-        ---------------------------
-        Path"""
-        from_, to = np.array(from_), np.array(to)
-        length = np.linalg.norm(from_ - to)
-        return Path(lambda pl: (1-pl/length)*from_ + pl/length*to, length)
-
-    def cut(self, length):
-        """Cut the path in two at a specific length along the path.
-
-        Parameters
-        --------------------------------------
-        length: float
-            The length along the path at which to cut.
-
-        Returns
-        -------------------------------------
-        (Path, Path)
-        
-        A tuple containing two paths. The first path contains the path upto length, while the second path contains the rest."""
-        return (Path(self.fun, length, [b for b in self.breakpoints if b <= length], name=self.name),
-                Path(lambda l: self.fun(l + length), self.path_length - length, [b - length for b in self.breakpoints if b >= length], name=self.name))
-    
-    def rectangle_xz(xmin, xmax, zmin, zmax):
-        """Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is 
-        counter clockwise around the y-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-        return Path.line([xmin, 0., zmin], [xmax, 0, zmin]) \
-            .line_to([xmax, 0, zmax]).line_to([xmin, 0., zmax]).close()
-     
-    def rectangle_yz(ymin, ymax, zmin, zmax):
-        """Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is 
-        counter clockwise around the x-axis.
-        
-        Parameters
-        ------------------------
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-
-        return Path.line([0., ymin, zmin], [0, ymin, zmax]) \
-            .line_to([0., ymax, zmax]).line_to([0., ymax, zmin]).close()
-     
-    def rectangle_xy(xmin, xmax, ymin, ymax):
-        """Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is 
-        counter clockwise around the z-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-        return Path.line([xmin, ymin, 0.], [xmin, ymax, 0.]) \
-            .line_to([xmax, ymax, 0.]).line_to([xmax, ymin, 0.]).close()
-    
-    def aperture(height, radius, extent, z=0.):
-        """Create an 'aperture'. Note that in a radially symmetric geometry
-        an aperture is basically a rectangle with the right side 'open'. Revolving
-        this path around the z-axis would generate a cylindircal hole in the center. 
-        This is the most basic model of an aperture.
-
-        Parameters
-        ------------------------
-        height: float
-            The height of the aperture
-        radius: float
-            The radius of the aperture hole (distance to the z-axis)
-        extent: float
-            The maximum x value
-        z: float
-            The z-coordinate of the center of the aperture
-
-        Returns
-        ------------------------
-        Path"""
-        return Path.line([extent, 0., -height/2], [radius, 0., -height/2])\
-                .line_to([radius, 0., height/2]).line_to([extent, 0., height/2]).move(dz=z)
-    
-    def __add__(self, other):
-        """Add two paths to create a PathCollection. Note that a PathCollection supports
-        a subset of the methods of Path (for example, movement, rotation and meshing). Use
-        the + operator to combine paths into a path collection: path1 + path2 + path3.
-
-        Returns
-        -------------------------
-        PathCollection"""
-         
-        if not isinstance(other, Path) and not isinstance(other, PathCollection):
-            return NotImplemented
-        
-        if isinstance(other, Path):
-            return PathCollection([self, other])
-        elif isinstance(other, PathCollection):
-            return PathCollection([self] + [other.paths])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False):
-        """Mesh the path, so it can be used in the BEM solver.
-
-        Parameters
-        --------------------------
-        mesh_size: float
-            Determines amount of elements in the mesh. A smaller
-            mesh size leads to more elements.
-        mesh_size_factor: float
-            Alternative way to specify the mesh size, which scales
-            with the dimensions of the geometry, and therefore more
-            easily translates between different geometries.
-        higher_order: bool
-            Whether to generate a higher order mesh. A higher order
-            produces curved line elements (determined by 4 points on
-            each curved element). The BEM solver supports higher order
-            elements in radial symmetric geometries only.
-
-        Returns
-        ----------------------------
-        Path"""
-        u = discretize_path(self.path_length, self.breakpoints, mesh_size, mesh_size_factor, N_factor=3 if higher_order else 1)
-        
-        N = len(u) 
-        points = np.zeros( (N, 3) )
-         
-        for i in range(N):
-            points[i] = self(u[i])
-         
-        if not higher_order:
-            lines = np.array([np.arange(N-1), np.arange(1, N)]).T
-        else:
-            assert N % 3 == 1
-            r = np.arange(N)
-            p0 = r[0:-1:3]
-            p1 = r[3::3]
-            p2 = r[1::3]
-            p3 = r[2::3]
-            lines = np.array([p0, p1, p2, p3]).T
-          
-        assert lines.dtype == np.int64 or lines.dtype == np.int32
-         
-        if self.name is not None:
-            physical_to_lines = {self.name:np.arange(len(lines))}
-        else:
-            physical_to_lines = {}
-        
-        return Mesh(points=points, lines=lines, physical_to_lines=physical_to_lines)
-
-

Ancestors

- -

Methods

-
-
-def __call__(self, t) -
-
-

Evaluate a point along the path.

-

Parameters

-
-
t : float
-
The length along the path.
-
-

Returns

-

(3,) float

-

Three dimensional point.

-
-
-def __rshift__(self, other) -
-
-

Combine two paths to create a single path. The endpoint of the first path needs -to match the starting point of the second path. This common point is marked as a breakpoint and -always included in the mesh. To use this function use the right shift operator (p1 >> p2).

-

Parameters

-
-
other : Path
-
The second path, to extend the current path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def add_phase(self, l) -
-
-

Add a phase to a closed path. A path is closed when the starting point is equal to the -end point. A phase of length l means that the path starts 'further down' the closed path.

-

Parameters

-
-
l : float
-
The phase (expressed as a path length). The resulting path starts l distance along the -original path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def aperture(height, radius, extent, z=0.0) -
-
-

Create an 'aperture'. Note that in a radially symmetric geometry -an aperture is basically a rectangle with the right side 'open'. Revolving -this path around the z-axis would generate a cylindircal hole in the center. -This is the most basic model of an aperture.

-

Parameters

-
-
height : float
-
The height of the aperture
-
radius : float
-
The radius of the aperture hole (distance to the z-axis)
-
extent : float
-
The maximum x value
-
z : float
-
The z-coordinate of the center of the aperture
-
-

Returns

-
-
Path
-
 
-
-
-
-def arc(center, start, end, reverse=False) -
-
-

Return an arc by specifying the center, start and end point.

-

Parameters

-
-
center : (3,) float
-
The center point of the arc.
-
start : (3,) float
-
The start point of the arc.
-
end : (3,) float
-
The endpoint of the arc.
-
-

Returns

-
-
Path
-
 
-
-
-
-def arc_to(self, center, end, reverse=False) -
-
-

Extend the current path using an arc.

-

Parameters

-
-
center : (3,) float
-
The center point of the arc.
-
end : (3,) float
-
The endpoint of the arc, shoud lie on a circle determined -by the given centerpoint and the current endpoint.
-
-

Returns

-
-
Path
-
 
-
-
-
-def average(self, fun) -
-
-

Average a function along the path, by integrating 1/l * fun(path(l)) with 0 <= l <= path length.

-

Parameters

-
-
fun : callable (3,) -> float
-
A function taking a three dimensional point and returning a float.
-
-

Returns

-
-
float
-
 
-
-

The average value of the function along the point.

-
-
-def circle_xy(x0, y0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the XY plane around the z-axis. Starting on the positive X-axis.

-

Parameters

-
-
y0 : float
-
x-coordinate of the center of the circle
-
y0 : float
-
y-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def circle_xz(x0, z0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the XZ plane around the x-axis. Starting on the positive x-axis.

-

Parameters

-
-
x0 : float
-
x-coordinate of the center of the circle
-
z0 : float
-
z-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def circle_yz(y0, z0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the YZ plane around the x-axis. Starting on the positive y-axis.

-

Parameters

-
-
y0 : float
-
x-coordinate of the center of the circle
-
z0 : float
-
z-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def close(self) -
-
-

Close the path, by making a straight line to the starting point.

-

Returns

-
-
Path
-
 
-
-
-
-def cut(self, length) -
-
-

Cut the path in two at a specific length along the path.

-

Parameters

-
-
length : float
-
The length along the path at which to cut.
-
-

Returns

-

(Path, Path)

-

A tuple containing two paths. The first path contains the path upto length, while the second path contains the rest.

-
-
-def ellipse(major, minor) -
-
-

Create a path along the outline of an ellipse. The ellipse lies -in the XY plane, and the path starts on the positive x-axis.

-

Parameters

-
-
major : float
-
The major axis of the ellipse (lies along the x-axis).
-
minor : float
-
The minor axis of the ellipse (lies along the y-axis).
-
-

Returns

-
-
Path
-
 
-
-
-
-def endpoint(self) -
-
-

Returns the endpoint of the path.

-

Returns

-

(3,) float

-

The endpoint of the path.

-
-
-def extrude(self, vector) -
-
-

Create a surface by extruding the path along a vector. The vector gives both -the length and the direction of the extrusion.

-

Parameters

-
-
vector : (3,) float
-
The direction and length (norm of the vector) to extrude by.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def extrude_by_path(self, p2) -
-
-

Create a surface by extruding the path along a second path. The second -path does not need to start along the first path. Imagine the surface created -by moving the first path along the second path.

-

Parameters

-
-
p2 : Path
-
The (second) path defining the extrusion.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def from_irregular_function(to_point, N=100, breakpoints=[]) -
-
-

Construct a path from a function that is of the form u -> point, where 0 <= u <= 1. -The length of the path is determined by integration.

-

Parameters

-
-
to_point : callable
-
A function accepting a number in the range [0, 1] and returns a the dimensional point.
-
N : int
-
Number of samples to use in the cubic spline interpolation.
-
breakpoints : float iterable
-
Points (0 <= u <= 1) on the path where the function is non-differentiable. These points -are always included in the resulting mesh.
-
-

Returns

-
-
Path
-
 
-
-
-
-def is_closed(self) -
-
-

Determine whether the path is closed, by comparing the starting and endpoint.

-

Returns

-

bool: True if the path is closed, False otherwise.

-
-
-def line(from_, to) -
-
-

Create a straight line between two points.

-

Parameters

-
-
from_ : (3,) float
-
The starting point of the path.
-
to : (3,) float
-
The endpoint of the path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def line_to(self, point) -
-
-

Extend the current path by a line from the current endpoint to the given point. -The given point is marked a breakpoint.

-

Parameters

-
-
point : (3,) float
-
The new endpoint.
-
-

Returns

-
-
Path
-
 
-
-
-
-def map_points(self, fun) -
-
-

Return a new function by mapping a function over points along the path (see GeometricObject). -The path length is assumed to stay the same after this operation.

-

Parameters

-
-
fun : callable (3,) -> (3,)
-
Function taking three dimensional points and returning three dimensional points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False) -
-
-

Mesh the path, so it can be used in the BEM solver.

-

Parameters

-
-
mesh_size : float
-
Determines amount of elements in the mesh. A smaller -mesh size leads to more elements.
-
mesh_size_factor : float
-
Alternative way to specify the mesh size, which scales -with the dimensions of the geometry, and therefore more -easily translates between different geometries.
-
higher_order : bool
-
Whether to generate a higher order mesh. A higher order -produces curved line elements (determined by 4 points on -each curved element). The BEM solver supports higher order -elements in radial symmetric geometries only.
-
-

Returns

-
-
Path
-
 
-
-
-
-def middle_point(self) -
-
-

Returns the midpoint of the path (in terms of length along the path.)

-

Returns

-

(3,) float

-

The point at the middle of the path.

-
-
-def rectangle_xy(xmin, xmax, ymin, ymax) -
-
-

Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is -counter clockwise around the z-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def rectangle_xz(xmin, xmax, zmin, zmax) -
-
-

Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is -counter clockwise around the y-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def rectangle_yz(ymin, ymax, zmin, zmax) -
-
-

Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is -counter clockwise around the x-axis.

-

Parameters

-
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def revolve_x(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the x-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def revolve_y(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the y-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def revolve_z(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the z-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def spline_through_points(points, N=100) -
-
-

Construct a path by fitting a cubic spline through the given points.

-

Parameters

-
-
points : (N, 3) ndarray of float
-
Three dimensional points through which the spline is fitted.
-
-

Returns

-
-
Path
-
 
-
-
-
-def starting_point(self) -
-
-

Returns the starting point of the path.

-

Returns

-

(3,) float

-

The starting point of the path.

-
-
-

Inherited members

- -
-
-class PathCollection -(paths) -
-
-

A PathCollection is a collection of Path. It can be created using the + operator (for example path1+path2). -Note that PathCollection is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class PathCollection(GeometricObject):
-    """A PathCollection is a collection of `Path`. It can be created using the + operator (for example path1+path2).
-    Note that `PathCollection` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-    
-    def __init__(self, paths):
-        assert all([isinstance(p, Path) for p in paths])
-        self.paths = paths
-        self.name = None
-    
-    def map_points(self, fun):
-        return PathCollection([p.map_points(fun) for p in self.paths])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False):
-        mesh = Mesh()
-        
-        for p in self.paths:
-            if self.name is not None:
-                p.name = self.name
-            mesh = mesh + p.mesh(mesh_size=mesh_size, mesh_size_factor=mesh_size_factor, higher_order=higher_order)
-
-        return mesh
-
-    def _map_to_surfaces(self, f, *args, **kwargs):
-        surfaces = []
-
-        for p in self.paths:
-            surfaces.append(f(p, *args, **kwargs))
-
-        return SurfaceCollection(surfaces)
-    
-    def __add__(self, other):
-        if not isinstance(other, Path) and not isinstance(other, PathCollection):
-            return NotImplemented
-        
-        if isinstance(other, Path):
-            return PathCollection(self.paths+[other])
-        else:
-            return PathCollection(self.paths+other.paths)
-      
-    def __iadd__(self, other):
-        assert isinstance(other, PathCollection) or isinstance(other, Path)
-
-        if isinstance(other, Path):
-            self.paths.append(other)
-        else:
-            self.paths.extend(other.paths)
-       
-    def revolve_x(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_x, angle=angle)
-    def revolve_y(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_y, angle=angle)
-    def revolve_z(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_z, angle=angle)
-    def extrude(self, vector):
-        return self._map_to_surface(Path.extrude, vector)
-    def extrude_by_path(self, p2):
-        return self._map_to_surface(Path.extrude_by_path, p2)
-
-

Ancestors

- -

Methods

-
-
-def extrude(self, vector) -
-
-
-
-
-def extrude_by_path(self, p2) -
-
-
-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False) -
-
-
-
-
-def revolve_x(self, angle=6.283185307179586) -
-
-
-
-
-def revolve_y(self, angle=6.283185307179586) -
-
-
-
-
-def revolve_z(self, angle=6.283185307179586) -
-
-
-
-
-

Inherited members

- -
-
-class Surface -(fun, path_length1, path_length2, breakpoints1=[], breakpoints2=[], name=None) -
-
-

A Surface is a mapping from two numbers to a three dimensional point. -Note that Surface is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Surface(GeometricObject):
-    """A Surface is a mapping from two numbers to a three dimensional point.
-    Note that `Surface` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-
-    def __init__(self, fun, path_length1, path_length2, breakpoints1=[], breakpoints2=[], name=None):
-        self.fun = fun
-        self.path_length1 = path_length1
-        self.path_length2 = path_length2
-        assert self.path_length1 > 0 and self.path_length2 > 0
-        self.breakpoints1 = breakpoints1
-        self.breakpoints2 = breakpoints2
-        self.name = name
-
-    def sections(self): 
-        # Iterate over the sections defined by
-        # the breakpoints
-        b1 = [0.] + self.breakpoints1 + [self.path_length1]
-        b2 = [0.] + self.breakpoints2 + [self.path_length2]
-
-        for u0, u1 in zip(b1[:-1], b1[1:]):
-            for v0, v1 in zip(b2[:-1], b2[1:]):
-                def fun(u, v, u0_=u0, v0_=v0):
-                    return self(u0_+u, v0_+v)
-                yield Surface(fun, u1-u0, v1-v0, [], [])
-       
-    def __call__(self, u, v):
-        return self.fun(u, v)
-
-    def map_points(self, fun):
-        return Surface(lambda u, v: fun(self(u, v)),
-            self.path_length1, self.path_length2,
-            self.breakpoints1, self.breakpoints2)
-     
-    def spanned_by_paths(path1, path2):
-        length1 = max(path1.path_length, path2.path_length)
-        
-        length_start = np.linalg.norm(path1.starting_point() - path2.starting_point())
-        length_final = np.linalg.norm(path1.endpoint() - path2.endpoint())
-        length2 = (length_start + length_final)/2
-         
-        def f(u, v):
-            p1 = path1(u/length1*path1.path_length) # u/l*p = b, u = l*b/p
-            p2 = path2(u/length1*path2.path_length)
-            return (1-v/length2)*p1 + v/length2*p2
-
-        breakpoints = sorted([length1*b/path1.path_length for b in path1.breakpoints] + \
-                                [length1*b/path2.path_length for b in path2.breakpoints])
-         
-        return Surface(f, length1, length2, breakpoints)
-
-    def sphere(radius):
-        
-        length1 = 2*pi*radius
-        length2 = pi*radius
-         
-        def f(u, v):
-            phi = u/radius
-            theta = v/radius
-            
-            return np.array([
-                radius*sin(theta)*cos(phi),
-                radius*sin(theta)*sin(phi),
-                radius*cos(theta)]) 
-        
-        return Surface(f, length1, length2)
-
-    def from_boundary_paths(p1, p2, p3, p4):
-        path_length_p1_and_p3 = (p1.path_length + p3.path_length)/2
-        path_length_p2_and_p4 = (p2.path_length + p4.path_length)/2
-
-        def f(u, v):
-            u /= path_length_p1_and_p3
-            v /= path_length_p2_and_p4
-            
-            a = (1-v)
-            b = (1-u)
-             
-            c = v
-            d = u
-            
-            return 1/2*(a*p1(u*p1.path_length) + \
-                        b*p4((1-v)*p4.path_length) + \
-                        c*p3((1-u)*p3.path_length) + \
-                        d*p2(v*p2.path_length))
-        
-        # Scale the breakpoints appropriately
-        b1 = sorted([b/p1.path_length * path_length_p1_and_p3 for b in p1.breakpoints] + \
-                [b/p3.path_length * path_length_p1_and_p3 for b in p3.breakpoints])
-        b2 = sorted([b/p2.path_length * path_length_p2_and_p4 for b in p2.breakpoints] + \
-                [b/p4.path_length * path_length_p2_and_p4 for b in p4.breakpoints])
-        
-        return Surface(f, path_length_p1_and_p3, path_length_p2_and_p4, b1, b2)
-     
-    def disk_xz(x0, z0, radius):
-        """Create a disk in the XZ plane.         
-        
-        Parameters
-        ------------------------
-        x0: float
-            x-coordiante of the center of the disk
-        z0: float
-            z-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [radius, 0.0, 0.0]).revolve_y()
-        return disk_at_origin.move(dx=x0, dz=z0)
-    
-    def disk_yz(y0, z0, radius):
-        """Create a disk in the YZ plane.         
-        
-        Parameters
-        ------------------------
-        y0: float
-            y-coordiante of the center of the disk
-        z0: float
-            z-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [0.0, radius, 0.0]).revolve_x()
-        return disk_at_origin.move(dy=y0, dz=z0)
-
-    def disk_xy(x0, y0, radius):
-        """Create a disk in the XY plane.
-        
-        Parameters
-        ------------------------
-        x0: float
-            x-coordiante of the center of the disk
-        y0: float
-            y-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [radius, 0.0, 0.0]).revolve_z()
-        return disk_at_origin.move(dx=x0, dy=y0)
-     
-    def rectangle_xz(xmin, xmax, zmin, zmax):
-        """Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is 
-        counter clockwise around the y-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([xmin, 0., zmin], [xmin, 0, zmax]).extrude([xmax-xmin, 0., 0.])
-     
-    def rectangle_yz(ymin, ymax, zmin, zmax):
-        """Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is 
-        counter clockwise around the x-axis.
-        
-        Parameters
-        ------------------------
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([0., ymin, zmin], [0., ymin, zmax]).extrude([0., ymax-ymin, 0.])
-     
-    def rectangle_xy(xmin, xmax, ymin, ymax):
-        """Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is 
-        counter clockwise around the z-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([xmin, ymin, 0.], [xmin, ymax, 0.]).extrude([xmax-xmin, 0., 0.])
-
-    def aperture(height, radius, extent, z=0.):
-        return Path.aperture(height, radius, extent, z=z).revolve_z()
-     
-    def __add__(self, other):
-        if not isinstance(other, Surface) and not isinstance(other, SurfaceCollection):
-            return NotImplemented
-
-        if isinstance(other, Surface):
-            return SurfaceCollection([self, other])
-        else:
-            return SurfaceCollection([self] + other.surfaces)
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None):
-          
-        if mesh_size is None:
-            path_length = min(self.path_length1, self.path_length2)
-             
-            mesh_size = path_length / 4
-
-            if mesh_size_factor is not None:
-                mesh_size /= sqrt(mesh_size_factor)
-         
-        return _mesh(self, mesh_size, name=self.name)
-
-

Ancestors

- -

Methods

-
-
-def aperture(height, radius, extent, z=0.0) -
-
-
-
-
-def disk_xy(x0, y0, radius) -
-
-

Create a disk in the XY plane.

-

Parameters

-
-
x0 : float
-
x-coordiante of the center of the disk
-
y0 : float
-
y-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def disk_xz(x0, z0, radius) -
-
-

Create a disk in the XZ plane. -

-

Parameters

-
-
x0 : float
-
x-coordiante of the center of the disk
-
z0 : float
-
z-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def disk_yz(y0, z0, radius) -
-
-

Create a disk in the YZ plane. -

-

Parameters

-
-
y0 : float
-
y-coordiante of the center of the disk
-
z0 : float
-
z-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def from_boundary_paths(p1, p2, p3, p4) -
-
-
-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None) -
-
-
-
-
-def rectangle_xy(xmin, xmax, ymin, ymax) -
-
-

Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is -counter clockwise around the z-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def rectangle_xz(xmin, xmax, zmin, zmax) -
-
-

Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is -counter clockwise around the y-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def rectangle_yz(ymin, ymax, zmin, zmax) -
-
-

Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is -counter clockwise around the x-axis.

-

Parameters

-
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def sections(self) -
-
-
-
-
-def spanned_by_paths(path1, path2) -
-
-
-
-
-def sphere(radius) -
-
-
-
-
-

Inherited members

- -
-
-class SurfaceCollection -(surfaces) -
-
-

A SurfaceCollection is a collection of Surface. It can be created using the + operator (for example surface1+surface2). -Note that SurfaceCollection is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class SurfaceCollection(GeometricObject):
-    """A SurfaceCollection is a collection of `Surface`. It can be created using the + operator (for example surface1+surface2).
-    Note that `SurfaceCollection` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-     
-    def __init__(self, surfaces):
-        assert all([isinstance(s, Surface) for s in surfaces])
-        self.surfaces = surfaces
-        self.name = None
-     
-    def map_points(self, fun):
-        return SurfaceCollection([s.map_points(fun) for s in self.surfaces])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, name=None):
-        mesh = Mesh()
-        
-        for s in self.surfaces:
-            if self.name is not None:
-                s.name = self.name
-            
-            mesh = mesh + s.mesh(mesh_size=mesh_size, mesh_size_factor=mesh_size_factor)
-         
-        return mesh
-     
-    def __add__(self, other):
-        if not isinstance(other, Surface) and not isinstance(other, SurfaceCollection):
-            return NotImplemented
-              
-        if isinstance(other, Surface):
-            return SurfaceCollection(self.surfaces+[other])
-        else:
-            return SurfaceCollection(self.surfaces+other.surfaces)
-     
-    def __iadd__(self, other):
-        assert isinstance(other, SurfaceCollection) or isinstance(other, Surface)
-        
-        if isinstance(other, Surface):
-            self.surfaces.append(other)
-        else:
-            self.surfaces.extend(other.surfaces)
-
-

Ancestors

- -

Methods

-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, name=None) -
-
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/index.html b/docs/docs/v0.7.0rc1/index.html deleted file mode 100644 index 60dab098..00000000 --- a/docs/docs/v0.7.0rc1/index.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -traceon API documentation - - - - - - - - - - - - - -
-
-
-

Package traceon

-
-
-

Welcome!

-

Traceon is a general software package used for numerical electron optics. Its main feature is the implementation of the Boundary Element Method (BEM) to quickly calculate the surface charge distribution. -The program supports both radial symmetry and general three-dimensional geometries. -Electron tracing can be done very quickly using accurate radial series interpolation in both geometries. -The electron trajectories obtained can help determine the aberrations of the optical components under study.

-

If you have any issues using the package, please open an issue on the Traceon Github page.

-

The software is currently distributed under the AGPLv3 license.

-

Usage

-

In general, one starts with the traceon.geometry module to create a mesh. For the BEM only the boundary of -electrodes needs to be meshed. So in 2D (radial symmetry) the mesh consists of line elements while in 3D the -mesh consists of triangles. -Next, one specifies a suitable excitation (voltages) using the traceon.excitation module. -The excited geometry can then be passed to the solve_direct() function, which computes the resulting field. -The field can be passed to the Tracer class to compute the trajectory of electrons moving through the field.

-

Validations

-

To make sure the software is correct, various problems from the literature with known solutions are analyzed using the Traceon software and the -results compared. In this manner it has been shown that the software produces very accurate results very quickly. The validations can be found in the -/validations directory in the Github project. After installing Traceon, the validations can be -executed as follows:

-
    git clone https://github.com/leon-vv/Traceon
-    cd traceon
-    python3 ./validation/edwards2007.py --help
-
-

Units

-

SI units are used throughout the codebase. Except for charge, which is stored as \frac{ \sigma}{ \epsilon_0} .

-
-
-

Sub-modules

-
-
traceon.excitation
-
-

The excitation module allows to specify the excitation (or element types) of the different physical groups (electrodes) -created with the …

-
-
traceon.focus
-
-

Module containing a single function to find the focus of a beam of electron trajecories.

-
-
traceon.geometry
-
-

The geometry module allows the creation of general meshes in 2D and 3D. -The builtin mesher uses so called parametric meshes, meaning -that for any …

-
-
traceon.interpolation
-
-
-
-
traceon.logging
-
-
-
-
traceon.mesher
-
-
-
-
traceon.plotting
-
-

The traceon.plotting module uses the vedo plotting library to provide some convenience functions -to show the line and triangle meshes generated by …

-
-
traceon.solver
-
-

The solver module uses the Boundary Element Method (BEM) to compute the surface charge distribution of a given -geometry and excitation. Once the …

-
-
traceon.tracing
-
-

The tracing module allows to trace electrons within any field type returned by the traceon.solver module. The tracing algorithm -used is RK45 with …

-
-
-
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/interpolation.html b/docs/docs/v0.7.0rc1/interpolation.html deleted file mode 100644 index 8a613735..00000000 --- a/docs/docs/v0.7.0rc1/interpolation.html +++ /dev/null @@ -1,369 +0,0 @@ - - - - - - -traceon.interpolation API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.interpolation

-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FieldAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-

An electrostatic field resulting from a radial series expansion around the optical axis. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the axial_derivative_interpolation methods. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldAxial(S.Field):
-    """An electrostatic field resulting from a radial series expansion around the optical axis. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `axial_derivative_interpolation` methods. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        N = len(z)
-        assert z.shape == (N,)
-        assert electrostatic_coeffs is None or len(electrostatic_coeffs)== N-1
-        assert magnetostatic_coeffs is None or len(magnetostatic_coeffs) == N-1
-        assert electrostatic_coeffs is not None or magnetostatic_coeffs is not None
-        
-        assert z[0] < z[-1], "z values in axial interpolation should be ascending"
-         
-        self.z = z
-        self.electrostatic_coeffs = electrostatic_coeffs if electrostatic_coeffs is not None else np.zeros_like(magnetostatic_coeffs)
-        self.magnetostatic_coeffs = magnetostatic_coeffs if magnetostatic_coeffs is not None else np.zeros_like(electrostatic_coeffs)
-        
-        self.has_electrostatic = np.any(self.electrostatic_coeffs != 0.)
-        self.has_magnetostatic = np.any(self.magnetostatic_coeffs != 0.)
-     
-    def is_electrostatic(self):
-        return self.has_electrostatic
-
-    def is_magnetostatic(self):
-        return self.has_magnetostatic
-     
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}, zmin={self.z[0]} mm, zmax={self.z[-1]} mm,\n\tNumber of samples on optical axis: {len(self.z)}>'
-     
-    def __add__(self, other):
-        if isinstance(other, FieldAxial):
-            assert np.array_equal(self.z, other.z), "Cannot add FieldAxial if optical axis sampling is different."
-            assert self.electrostatic_coeffs.shape == other.electrostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            assert self.magnetostatic_coeffs.shape == other.magnetostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            return self.__class__(self.z, self.electrostatic_coeffs+other.electrostatic_coeffs, self.magnetostatic_coeffs + other.magnetostatic_coeffs)
-         
-        return NotImpemented
-    
-    def __sub__(self, other):
-        return self.__add__(-other)
-    
-    def __radd__(self, other):
-        return self.__add__(other)
-     
-    def __mul__(self, other):
-        if isinstance(other, int) or isinstance(other, float):
-            return self.__class__(self.z, other*self.electrostatic_coeffs, other*self.magnetostatic_coeffs)
-         
-        return NotImpemented
-    
-    def __neg__(self):
-        return -1*self
-    
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-

Inherited members

- -
-
-class FieldRadialAxial -(field, zmin, zmax, N=None) -
-
-
-
- -Expand source code - -
class FieldRadialAxial(FieldAxial):
-    """ """
-    def __init__(self, field, zmin, zmax, N=None):
-        assert isinstance(field, S.FieldRadialBEM)
-
-        z, electrostatic_coeffs, magnetostatic_coeffs = FieldRadialAxial._get_interpolation_coefficients(field, zmin, zmax, N=N)
-        
-        super().__init__(z, electrostatic_coeffs, magnetostatic_coeffs)
-        
-        assert self.electrostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        assert self.magnetostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-
-    def _get_interpolation_coefficients(field, zmin, zmax, N=None):
-        assert zmax > zmin, "zmax should be bigger than zmin"
-
-        N_charges = max(len(field.electrostatic_point_charges.charges), len(field.magnetostatic_point_charges.charges))
-        N = N if N is not None else int(FACTOR_AXIAL_DERIV_SAMPLING_2D*N_charges)
-        z = np.linspace(zmin, zmax, N)
-        
-        st = time.time()
-        elec_derivs = np.concatenate(util.split_collect(field.get_electrostatic_axial_potential_derivatives, z), axis=0)
-        elec_coeffs = _quintic_spline_coefficients(z, elec_derivs.T)
-        
-        mag_derivs = np.concatenate(util.split_collect(field.get_magnetostatic_axial_potential_derivatives, z), axis=0)
-        mag_coeffs = _quintic_spline_coefficients(z, mag_derivs.T)
-        
-        logging.log_info(f'Computing derivative interpolation took {(time.time()-st)*1000:.2f} ms ({len(z)} items)')
-
-        return z, elec_coeffs, mag_coeffs
-     
-    def electrostatic_field_at_point(self, point):
-        """
-        Compute the electric field, \( \\vec{E} = -\\nabla \phi \)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the r and z directions.
-        """
-        assert point.shape == (2,)
-        return backend.field_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_field_at_point(self, point):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,)
-        return backend.field_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-     
-    def electrostatic_potential_at_point(self, point):
-        """
-        Compute the electrostatic potential (close to the axis).
-
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the potential.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,)
-        return backend.potential_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_potential_at_point(self, point):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\)) close to the axis
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        assert point.shape == (2,)
-        return backend.potential_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialAxial(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def electrostatic_field_at_point(self, point) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the r and z directions.

-
-
-def electrostatic_potential_at_point(self, point) -
-
-

Compute the electrostatic potential (close to the axis).

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the potential.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi ) close to the axis

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/mesher.html b/docs/docs/v0.7.0rc1/mesher.html deleted file mode 100644 index 82aa3a95..00000000 --- a/docs/docs/v0.7.0rc1/mesher.html +++ /dev/null @@ -1,906 +0,0 @@ - - - - - - -traceon.mesher API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.mesher

-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class GeometricObject -
-
-

The Mesh class (and the classes defined in traceon.geometry) are subclasses -of GeometricObject. This means that they all can be moved, rotated, mirrored.

-
- -Expand source code - -
class GeometricObject:
-    """The Mesh class (and the classes defined in `traceon.geometry`) are subclasses
-    of GeometricObject. This means that they all can be moved, rotated, mirrored."""
-    
-    def map_points(self, fun):
-        """Create a new geometric object, by mapping each point by a function.
-        
-        Parameters
-        -------------------------
-        fun: (3,) float -> (3,) float
-            Function taking a three dimensional point and returning a 
-            three dimensional point.
-
-        Returns
-        ------------------------
-        GeometricObject
-
-        This function returns the same type as the object on which this method was called."""
-        pass
-    
-    def move(self, dx=0., dy=0., dz=0.):
-        """Move along x, y or z axis.
-
-        Parameters
-        ---------------------------
-        dx: float
-            Amount to move along the x-axis.
-        dy: float
-            Amount to move along the y-axis.
-        dz: float
-            Amount to move along the z-axis.
-
-        Returns
-        ---------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-    
-        assert all([isinstance(d, float) or isinstance(d, int) for d in [dx, dy, dz]])
-        return self.map_points(lambda p: p + np.array([dx, dy, dz]))
-     
-    def rotate(self, Rx=0., Ry=0., Rz=0., origin=[0., 0., 0.]):
-        """Rotate counter clockwise around the x, y or z axis. Only one axis supported at the same time
-        (rotations do not commute).
-
-        Parameters
-        ------------------------------------
-        Rx: float
-            Amount to rotate around the x-axis (radians).
-        Ry: float
-            Amount to rotate around the y-axis (radians).
-        Rz: float
-            Amount to rotate around the z-axis (radians).
-        origin: (3,) float
-            Point around which to rotate, which is the origin by default.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        
-        assert sum([Rx==0., Ry==0., Rz==0.]) >= 2, "Only supply one axis of rotation"
-        origin = np.array(origin)
-        assert origin.shape == (3,), "Please supply a 3D point for origin"
-         
-        if Rx != 0.:
-            matrix = np.array([[1, 0, 0],
-                [0, np.cos(Rx), -np.sin(Rx)],
-                [0, np.sin(Rx), np.cos(Rx)]])
-        elif Ry != 0.:
-            matrix = np.array([[np.cos(Ry), 0, np.sin(Ry)],
-                [0, 1, 0],
-                [-np.sin(Ry), 0, np.cos(Ry)]])
-        elif Rz != 0.:
-            matrix = np.array([[np.cos(Rz), -np.sin(Rz), 0],
-                [np.sin(Rz), np.cos(Rz), 0],
-                [0, 0, 1]])
-
-        return self.map_points(lambda p: origin + matrix @ (p - origin))
-
-    def mirror_xz(self):
-        """Mirror object in the XZ plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([p[0], -p[1], p[2]]))
-     
-    def mirror_yz(self):
-        """Mirror object in the YZ plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([-p[0], p[1], p[2]]))
-    
-    def mirror_xy(self):
-        """Mirror object in the XY plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([p[0], p[1], -p[2]]))
-
-

Subclasses

- -

Methods

-
-
-def map_points(self, fun) -
-
-

Create a new geometric object, by mapping each point by a function.

-

Parameters

-
-
fun : (3,) float -> (3,) float
-
Function taking a three dimensional point and returning a -three dimensional point.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_xy(self) -
-
-

Mirror object in the XY plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_xz(self) -
-
-

Mirror object in the XZ plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_yz(self) -
-
-

Mirror object in the YZ plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def move(self, dx=0.0, dy=0.0, dz=0.0) -
-
-

Move along x, y or z axis.

-

Parameters

-
-
dx : float
-
Amount to move along the x-axis.
-
dy : float
-
Amount to move along the y-axis.
-
dz : float
-
Amount to move along the z-axis.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def rotate(self, Rx=0.0, Ry=0.0, Rz=0.0, origin=[0.0, 0.0, 0.0]) -
-
-

Rotate counter clockwise around the x, y or z axis. Only one axis supported at the same time -(rotations do not commute).

-

Parameters

-
-
Rx : float
-
Amount to rotate around the x-axis (radians).
-
Ry : float
-
Amount to rotate around the y-axis (radians).
-
Rz : float
-
Amount to rotate around the z-axis (radians).
-
origin : (3,) float
-
Point around which to rotate, which is the origin by default.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-
-
-class Mesh -(points=[], lines=[], triangles=[], physical_to_lines={}, physical_to_triangles={}) -
-
-

Mesh containing lines and triangles. Groups of lines or triangles can be named. These -names are later used to apply the correct excitation. Line elements can be curved (or 'higher order'), -in which case they are represented by four points per element. -Note that Mesh is a subclass of -GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Mesh(Saveable, GeometricObject):
-    """Mesh containing lines and triangles. Groups of lines or triangles can be named. These
-    names are later used to apply the correct excitation. Line elements can be curved (or 'higher order'), 
-    in which case they are represented by four points per element.  Note that `Mesh` is a subclass of
-    `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-     
-    def __init__(self,
-            points=[],
-            lines=[],
-            triangles=[],
-            physical_to_lines={},
-            physical_to_triangles={}):
-        
-        # Ensure the correct shape even if empty arrays
-        if len(points):
-            self.points = np.array(points, dtype=np.float64)
-        else:
-            self.points = np.empty((0,3), dtype=np.float64)
-         
-        if len(lines) or (isinstance(lines, np.ndarray) and len(lines.shape)==2):
-            self.lines = np.array(lines, dtype=np.uint64)
-        else:
-            self.lines = np.empty((0,2), dtype=np.uint64)
-    
-        if len(triangles):
-            self.triangles = np.array(triangles, dtype=np.uint64)
-        else:
-            self.triangles = np.empty((0, 3), dtype=np.uint64)
-         
-        self.physical_to_lines = physical_to_lines.copy()
-        self.physical_to_triangles = physical_to_triangles.copy()
-
-        self._remove_degenerate_triangles()
-        
-        assert np.all( (0 <= self.lines) & (self.lines < len(self.points)) ), "Lines reference points outside points array"
-        assert np.all( (0 <= self.triangles) & (self.triangles < len(self.points)) ), "Triangles reference points outside points array"
-        assert np.all([np.all( (0 <= group) & (group < len(self.lines)) ) for group in self.physical_to_lines.values()])
-        assert np.all([np.all( (0 <= group) & (group < len(self.triangles)) ) for group in self.physical_to_triangles.values()])
-        assert not len(self.lines) or self.lines.shape[1] in [2,4], "Lines should contain either 2 or 4 points."
-        assert not len(self.triangles) or self.triangles.shape[1] in [3,6], "Triangles should contain either 3 or 6 points"
-    
-    def is_higher_order(self):
-        """Whether the mesh contains higher order elements.
-
-        Returns
-        ----------------------------
-        bool"""
-        return isinstance(self.lines, np.ndarray) and len(self.lines.shape) == 2 and self.lines.shape[1] == 4
-    
-    def map_points(self, fun):
-        """See `GeometricObject`
-
-        """
-        new_points = np.empty_like(self.points)
-        for i in range(len(self.points)):
-            new_points[i] = fun(self.points[i])
-        assert new_points.shape == self.points.shape and new_points.dtype == self.points.dtype
-        
-        return Mesh(new_points, self.lines, self.triangles, self.physical_to_lines, self.physical_to_triangles)
-    
-    def _remove_degenerate_triangles(self):
-        areas = triangle_areas(self.points[self.triangles[:,:3]])
-        degenerate = areas < 1e-12
-        map_index = np.arange(len(self.triangles)) - np.cumsum(degenerate)
-         
-        self.triangles = self.triangles[~degenerate]
-        
-        for k in self.physical_to_triangles.keys():
-            v = self.physical_to_triangles[k]
-            self.physical_to_triangles[k] = map_index[v[~degenerate[v]]]
-         
-        if np.any(degenerate):
-            log_debug(f'Removed {sum(degenerate)} degenerate triangles')
-    
-    def _merge_dicts(dict1, dict2):
-        dict_ = {}
-        
-        for (k, v) in chain(dict1.items(), dict2.items()):
-            if k in dict_:
-                dict_[k] = np.concatenate( (dict_[k], v), axis=0)
-            else:
-                dict_[k] = v
-
-        return dict_
-     
-    def __add__(self, other):
-        """Add meshes together, using the + operator (mesh1 + mesh2).
-        
-        Returns
-        ------------------------------
-        Mesh
-
-        A new mesh consisting of the elements of the added meshes"""
-        if not isinstance(other, Mesh):
-            return NotImplemented
-         
-        N_points = len(self.points)
-        N_lines = len(self.lines)
-        N_triangles = len(self.triangles)
-         
-        points = _concat_arrays(self.points, other.points)
-        lines = _concat_arrays(self.lines, other.lines+N_points)
-        triangles = _concat_arrays(self.triangles, other.triangles+N_points)
-
-        other_physical_to_lines = {k:(v+N_lines) for k, v in other.physical_to_lines.items()}
-        other_physical_to_triangles = {k:(v+N_triangles) for k, v in other.physical_to_triangles.items()}
-         
-        physical_lines = Mesh._merge_dicts(self.physical_to_lines, other_physical_to_lines)
-        physical_triangles = Mesh._merge_dicts(self.physical_to_triangles, other_physical_to_triangles)
-         
-        return Mesh(points=points,
-                    lines=lines,
-                    triangles=triangles,
-                    physical_to_lines=physical_lines,
-                    physical_to_triangles=physical_triangles)
-     
-    def extract_physical_group(self, name):
-        """Extract a named group from the mesh.
-
-        Parameters
-        ---------------------------
-        name: str
-            Name of the group of elements
-
-        Returns
-        --------------------------
-        Mesh
-
-        Subset of the mesh consisting only of the elements with the given name."""
-        assert name in self.physical_to_lines or name in self.physical_to_triangles, "Physical group not in mesh, so cannot extract"
-
-        if name in self.physical_to_lines:
-            elements = self.lines
-            physical = self.physical_to_lines
-        elif name in self.physical_to_triangles:
-            elements = self.triangles
-            physical = self.physical_to_triangles
-         
-        elements_indices = np.unique(physical[name])
-        elements = elements[elements_indices]
-          
-        points_mask = np.full(len(self.points), False)
-        points_mask[elements] = True
-        points = self.points[points_mask]
-          
-        new_index = np.cumsum(points_mask) - 1
-        elements = new_index[elements]
-        physical_to_elements = {name:np.arange(len(elements))}
-         
-        if name in self.physical_to_lines:
-            return Mesh(points=points, lines=elements, physical_to_lines=physical_to_elements)
-        elif name in self.physical_to_triangles:
-            return Mesh(points=points, triangles=triangles, physical_to_triangles=physical_to_elements)
-     
-    def read_file(filename,  name=None):
-        """Create a mesh from a given file. All formats supported by meshio are accepted.
-
-        Parameters
-        ------------------------------
-        filename: str
-            Path of the file to convert to Mesh
-        name: str
-            (optional) name to assign to all elements readed
-
-        Returns
-        -------------------------------
-        Mesh"""
-        meshio_obj = meshio.read(filename)
-        mesh = Mesh.from_meshio(meshio_obj)
-         
-        if name is not None:
-            mesh.physical_to_lines[name] = np.arange(len(mesh.lines))
-            mesh.physical_to_triangles[name] = np.arange(len(mesh.triangles))
-         
-        return mesh
-     
-    def write_file(self, filename):
-        """Write a mesh to a given file. The format is determined from the file extension.
-        All formats supported by meshio are supported.
-
-        Parameters
-        ----------------------------------
-        filename: str
-            The name of the file to write the mesh to."""
-        meshio_obj = self.to_meshio()
-        meshio_obj.write(filename)
-    
-    def write(self, filename):
-        self.write_file(filename)
-        
-     
-    def to_meshio(self):
-        """Convert the Mesh to a meshio object.
-
-        Returns
-        ------------------------------------
-        meshio.Mesh"""
-        to_write = []
-        
-        if len(self.lines):
-            line_type = 'line' if self.lines.shape[1] == 2 else 'line4'
-            to_write.append( (line_type, self.lines) )
-        
-        if len(self.triangles):
-            triangle_type = 'triangle' if self.triangles.shape[1] == 3 else 'triangle6'
-            to_write.append( (triangle_type, self.triangles) )
-        
-        return meshio.Mesh(self.points, to_write)
-     
-    def from_meshio(mesh):
-        """Create a Traceon mesh from a meshio.Mesh object.
-
-        Parameters
-        --------------------------
-        mesh: meshio.Mesh
-            The mesh to convert to a Traceon mesh
-
-        Returns
-        -------------------------
-        Mesh"""
-        def extract(type_):
-            elements = mesh.cells_dict[type_]
-            physical = {k:v[type_] for k,v in mesh.cell_sets_dict.items() if type_ in v}
-            return elements, physical
-        
-        lines, physical_lines = [], {}
-        triangles, physical_triangles = [], {}
-        
-        if 'line' in mesh.cells_dict:
-            lines, physical_lines = extract('line')
-        elif 'line4' in mesh.cells_dict:
-            lines, physical_lines = extract('line4')
-        
-        if 'triangle' in mesh.cells_dict:
-            triangles, physical_triangles = extract('triangle')
-        elif 'triangle6' in mesh.cells_dict:
-            triangles, physical_triangles = extract('triangle6')
-        
-        return Mesh(points=mesh.points,
-            lines=lines, physical_to_lines=physical_lines,
-            triangles=triangles, physical_to_triangles=physical_triangles)
-     
-    def is_3d(self):
-        """Check if the mesh is three dimensional by checking whether any z coordinate is non-zero.
-
-        Returns
-        ----------------
-        bool
-
-        Whether the mesh is three dimensional"""
-        return np.any(self.points[:, 1] != 0.)
-    
-    def is_2d(self):
-        """Check if the mesh is two dimensional, by checking that all z coordinates are zero.
-        
-        Returns
-        ----------------
-        bool
-
-        Whether the mesh is two dimensional"""
-        return np.all(self.points[:, 1] == 0.)
-    
-    def flip_normals(self):
-        """Flip the normals in the mesh by inverting the 'orientation' of the elements.
-
-        Returns
-        ----------------------------
-        Mesh"""
-        lines = self.lines
-        triangles = self.triangles
-        
-        # Flip the orientation of the lines
-        if lines.shape[1] == 4:
-            p0, p1, p2, p3 = lines.T
-            lines = np.array([p1, p0, p3, p2]).T
-        else:
-            p0, p1 = lines.T
-            lines = np.array([p1, p0]).T
-          
-        # Flip the orientation of the triangles
-        if triangles.shape[1] == 6:
-            p0, p1, p2, p3, p4, p5 = triangles.T
-            triangles = np.array([p0, p2, p1, p5, p4, p3]).T
-        else:
-            p0, p1, p2 = triangles.T
-            triangles = np.array([p0, p2, p1]).T
-        
-        return Mesh(self.points, lines, triangles,
-            physical_to_lines=self.physical_to_lines,
-            physical_to_triangles=self.physical_to_triangles)
-     
-    def remove_lines(self):
-        """Remove all the lines from the mesh.
-
-        Returns
-        -----------------------------
-        Mesh"""
-        return Mesh(self.points, triangles=self.triangles, physical_to_triangles=self.physical_to_triangles)
-    
-    def remove_triangles(self):
-        """Remove all triangles from the mesh.
-
-        Returns
-        -------------------------------------
-        Mesh"""
-        return Mesh(self.points, lines=self.lines, physical_to_lines=self.physical_to_lines)
-     
-    def get_electrodes(self):
-        """Get the names of all the named groups (i.e. electrodes) in the mesh
-         
-        Returns
-        ---------
-        str iterable
-
-        Names
-        """
-        return list(self.physical_to_lines.keys()) + list(self.physical_to_triangles.keys())
-     
-    def _lines_to_higher_order(points, elements):
-        N_elements = len(elements)
-        N_points = len(points)
-         
-        v0, v1 = elements.T
-        p2 = points[v0] + (points[v1] - points[v0]) * 1/3
-        p3 = points[v0] + (points[v1] - points[v0]) * 2/3
-         
-        assert all(p.shape == (N_elements, points.shape[1]) for p in [p2, p3])
-         
-        points = np.concatenate( (points, p2, p3), axis=0)
-          
-        elements = np.array([
-            elements[:, 0], elements[:, 1], 
-            np.arange(N_points, N_points + N_elements, dtype=np.uint64),
-            np.arange(N_points + N_elements, N_points + 2*N_elements, dtype=np.uint64)]).T
-         
-        assert np.allclose(p2, points[elements[:, 2]]) and np.allclose(p3, points[elements[:, 3]])
-        return points, elements
-
-
-    def _to_higher_order_mesh(self):
-        # The matrix solver currently only works with higher order meshes.
-        # We can however convert a simple mesh easily to a higher order mesh, and solve that.
-        
-        points, lines, triangles = self.points, self.lines, self.triangles
-
-        if not len(lines):
-            lines = np.empty( (0, 4), dtype=np.float64)
-        elif len(lines) and lines.shape[1] == 2:
-            points, lines = Mesh._lines_to_higher_order(points, lines)
-        
-        assert lines.shape == (len(lines), 4)
-
-        return Mesh(points=points,
-            lines=lines, physical_to_lines=self.physical_to_lines,
-            triangles=triangles, physical_to_triangles=self.physical_to_triangles)
-     
-    def __str__(self):
-        physical_lines = ', '.join(self.physical_to_lines.keys())
-        physical_lines_nums = ', '.join([str(len(self.physical_to_lines[n])) for n in self.physical_to_lines.keys()])
-        physical_triangles = ', '.join(self.physical_to_triangles.keys())
-        physical_triangles_nums = ', '.join([str(len(self.physical_to_triangles[n])) for n in self.physical_to_triangles.keys()])
-        
-        return f'<Traceon Mesh,\n' \
-            f'\tNumber of points: {len(self.points)}\n' \
-            f'\tNumber of lines: {len(self.lines)}\n' \
-            f'\tNumber of triangles: {len(self.triangles)}\n' \
-            f'\tPhysical lines: {physical_lines}\n' \
-            f'\tElements in physical line groups: {physical_lines_nums}\n' \
-            f'\tPhysical triangles: {physical_triangles}\n' \
-            f'\tElements in physical triangle groups: {physical_triangles_nums}>'
-
-

Ancestors

- -

Methods

-
-
-def extract_physical_group(self, name) -
-
-

Extract a named group from the mesh.

-

Parameters

-
-
name : str
-
Name of the group of elements
-
-

Returns

-
-
Mesh
-
 
-
-

Subset of the mesh consisting only of the elements with the given name.

-
-
-def flip_normals(self) -
-
-

Flip the normals in the mesh by inverting the 'orientation' of the elements.

-

Returns

-
-
Mesh
-
 
-
-
-
-def from_meshio(mesh) -
-
-

Create a Traceon mesh from a meshio.Mesh object.

-

Parameters

-
-
mesh : meshio.Mesh
-
The mesh to convert to a Traceon mesh
-
-

Returns

-
-
Mesh
-
 
-
-
-
-def get_electrodes(self) -
-
-

Get the names of all the named groups (i.e. electrodes) in the mesh

-

Returns

-
-
str iterable
-
 
-
Names
-
 
-
-
-
-def is_2d(self) -
-
-

Check if the mesh is two dimensional, by checking that all z coordinates are zero.

-

Returns

-
-
bool
-
 
-
Whether the mesh is two dimensional
-
 
-
-
-
-def is_3d(self) -
-
-

Check if the mesh is three dimensional by checking whether any z coordinate is non-zero.

-

Returns

-
-
bool
-
 
-
Whether the mesh is three dimensional
-
 
-
-
-
-def is_higher_order(self) -
-
-

Whether the mesh contains higher order elements.

-

Returns

-
-
bool
-
 
-
-
-
-def map_points(self, fun) -
-
- -
-
-def read_file(filename, name=None) -
-
-

Create a mesh from a given file. All formats supported by meshio are accepted.

-

Parameters

-
-
filename : str
-
Path of the file to convert to Mesh
-
name : str
-
(optional) name to assign to all elements readed
-
-

Returns

-
-
Mesh
-
 
-
-
-
-def remove_lines(self) -
-
-

Remove all the lines from the mesh.

-

Returns

-
-
Mesh
-
 
-
-
-
-def remove_triangles(self) -
-
-

Remove all triangles from the mesh.

-

Returns

-
-
Mesh
-
 
-
-
-
-def to_meshio(self) -
-
-

Convert the Mesh to a meshio object.

-

Returns

-
-
meshio.Mesh
-
 
-
-
-
-def write(self, filename) -
-
-

Write a mesh to a file. The pickle module will be used -to save the Geometry object.

-

Args

-
-
filename
-
name of the file
-
-
-
-def write_file(self, filename) -
-
-

Write a mesh to a given file. The format is determined from the file extension. -All formats supported by meshio are supported.

-

Parameters

-
-
filename : str
-
The name of the file to write the mesh to.
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/solver.html b/docs/docs/v0.7.0rc1/solver.html deleted file mode 100644 index 15b25d36..00000000 --- a/docs/docs/v0.7.0rc1/solver.html +++ /dev/null @@ -1,1524 +0,0 @@ - - - - - - -traceon.solver API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.solver

-
-
-

The solver module uses the Boundary Element Method (BEM) to compute the surface charge distribution of a given -geometry and excitation. Once the surface charge distribution is known, the field at any arbitrary position in space -can be calculated by integration over the charged boundary. However, doing a field evaluation in this manner is very slow -as for every field evaluation an iteration needs to be done over all elements in the mesh. Especially for particle tracing it -is crucial that the field evaluation can be done faster. To achieve this, interpolation techniques can be used.

-

The solver package offers interpolation in the form of radial series expansions to drastically increase the speed of ray tracing. For -this consider the axial_derivative_interpolation methods documented below.

-

Radial series expansion in cylindrical symmetry

-

Let \phi_0(z) be the potential along the optical axis. We can express the potential around the optical axis as:

-

-\phi = \phi_0(z_0) - \frac{r^2}{4} \frac{\partial \phi_0^2}{\partial z^2} + \frac{r^4}{64} \frac{\partial^4 \phi_0}{\partial z^4} - \frac{r^6}{2304} \frac{\partial \phi_0^6}{\partial z^6} + \cdots -

-

Therefore, if we can efficiently compute the axial potential derivatives \frac{\partial \phi_0^n}{\partial z^n} we can compute the potential and therefore the fields around the optical axis. -For the derivatives of \phi_0(z) closed form formulas exist in the case of radially symmetric geometries, see for example formula 13.16a in [1]. Traceon uses a recursive version of these formulas to -very efficiently compute the axial derivatives of the potential.

-

Radial series expansion in 3D

-

In a general three dimensional geometry the potential will be dependent not only on the distance from the optical axis but also on the angle \theta around the optical axis -at which the potential is sampled. It turns out (equation (35, 24) in [2]) the potential can be written as follows:

-

-\phi = \sum_{\nu=0}^\infty \sum_{m=0}^\infty r^{2\nu + m} \left( A^\nu_m \cos(m\theta) + B^\nu_m \sin(m\theta) \right) -

-

The A^\nu_m and B^\nu_m coefficients can be expressed in directional derivatives perpendicular to the optical axis, analogous to the radial symmetric case. The -mathematics of calculating these coefficients quickly and accurately gets quite involved, but all details have been abstracted away from the user.

-

References

-

[1] P. Hawkes, E. Kasper. Principles of Electron Optics. Volume one: Basic Geometrical Optics. 2018.

-

[2] W. Glaser. Grundlagen der Elektronenoptik. 1952.

-
-
-
-
-
-
-

Functions

-
-
-def solve_direct(excitation) -
-
-

Solve for the charges on the surface of the geometry by using a direct method and taking -into account the specified excitation.

-

Parameters

-
-
excitation : Excitation
-
The excitation that produces the resulting field.
-
-

Returns

-

A FieldRadialBEM if the geometry (contained in the given excitation) is radially symmetric. If the geometry is a three -dimensional geometry Field3D_BEM is returned.

-
-
-def solve_direct_superposition(excitation) -
-
-

superposition : bool -When using superposition the function returns multiple fields. Each field corresponds with a unity excitation (1V) -of a physical group that was previously assigned a non-zero fixed voltage value. This is useful when a geometry needs -to be analyzed for many different voltage settings. In this case taking a linear superposition of the returned fields -allows to select a different voltage 'setting' without inducing any computational cost. There is no computational cost -involved in using superposition=True since a direct solver is used which easily allows for multiple right hand sides (the -matrix does not have to be inverted multiple times). However, voltage functions are invalid in the superposition process (position dependent voltages).

-
-
-
-
-

Classes

-
-
-class Field -
-
-
-
- -Expand source code - -
class Field:
-    def field_at_point(self, point):
-        """Convenience function for getting the field in the case that the field is purely electrostatic
-        or magneotstatic. Automatically picks one of `electrostatic_field_at_point` or `magnetostatic_field_at_point`.
-        Throws an exception when the field is both electrostatic and magnetostatic.
-
-        Parameters
-        ---------------------
-        point: (2,) or (3,) np.ndarray of float64
-
-        Returns
-        --------------------
-        (2,) or (3,) np.ndarray of float64. The electrostatic field \\(\\vec{E}\\) or the magnetostatic field \\(\\vec{H}\\).
-        """
-        elec, mag = self.is_electrostatic(), self.is_magnetostatic()
-        
-        if elec and not mag:
-            return self.electrostatic_field_at_point(point)
-        elif not elec and mag:
-            return self.magnetostatic_field_at_point(point)
-         
-        raise RuntimeError("Cannot use field_at_point when both electric and magnetic fields are present, " \
-            "use electrostatic_field_at_point or magnetostatic_potential_at_point")
-     
-    def potential_at_point(self, point):
-        """Convenience function for getting the potential in the case that the field is purely electrostatic
-        or magneotstatic. Automatically picks one of `electrostatic_potential_at_point` or `magnetostatic_potential_at_point`.
-        Throws an exception when the field is both electrostatic and magnetostatic.
-         
-        Parameters
-        ---------------------
-        point: (2,) or (3,) np.ndarray of float64
-
-        Returns
-        --------------------
-        float. The electrostatic potential (unit Volt) or magnetostaic scalar potential (unit Ampere)
-        """
-        elec, mag = self.is_electrostatic(), self.is_magnetostatic()
-         
-        if elec and not mag:
-            return self.electrostatic_potential_at_point(point)
-        elif not elec and mag:
-            return self.magnetostatic_potential_at_point(point)
-         
-        raise RuntimeError("Cannot use potential_at_point when both electric and magnetic fields are present, " \
-            "use electrostatic_potential_at_point or magnetostatic_potential_at_point")
-
-

Subclasses

- -

Methods

-
-
-def field_at_point(self, point) -
-
-

Convenience function for getting the field in the case that the field is purely electrostatic -or magneotstatic. Automatically picks one of electrostatic_field_at_point or magnetostatic_field_at_point. -Throws an exception when the field is both electrostatic and magnetostatic.

-

Parameters

-
-
point : (2,) or (3,) np.ndarray of float64
-
 
-
-

Returns

-

(2,) or (3,) np.ndarray of float64. The electrostatic field \vec{E} or the magnetostatic field \vec{H}.

-
-
-def potential_at_point(self, point) -
-
-

Convenience function for getting the potential in the case that the field is purely electrostatic -or magneotstatic. Automatically picks one of electrostatic_potential_at_point or magnetostatic_potential_at_point. -Throws an exception when the field is both electrostatic and magnetostatic.

-

Parameters

-
-
point : (2,) or (3,) np.ndarray of float64
-
 
-
-

Returns

-
-
float. The electrostatic potential (unit Volt) or magnetostaic scalar potential (unit Ampere)
-
 
-
-
-
-
-
-class Field3DAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-

Field computed using a radial series expansion around the optical axis (z-axis). See comments at the start of this page.

-
- -Expand source code - -
class Field3DAxial(FieldAxial):
-    """Field computed using a radial series expansion around the optical axis (z-axis). See comments at the start of this page.
-     """
-    
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        super().__init__(z, electrostatic_coeffs, magnetostatic_coeffs)
-        
-        assert electrostatic_coeffs.shape == (len(z)-1, 2, backend.NU_MAX, backend.M_MAX, 4)
-        assert magnetostatic_coeffs.shape == (len(z)-1, 2, backend.NU_MAX, backend.M_MAX, 4)
-        
-        self.symmetry = E.Symmetry.THREE_D
-     
-    def electrostatic_field_at_point(self, point):
-        """
-        Compute the electric field, \( \\vec{E} = -\\nabla \phi \)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the x, y and z directions.
-        """
-        assert point.shape == (3,)
-        return backend.field_3d_derivs(point, self.z, self.electrostatic_coeffs)
-     
-    def electrostatic_potential_at_point(self, point):
-        """
-        Compute the electrostatic potential.
-
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the potential.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (3,)
-        return backend.potential_3d_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_field_at_point(self, point):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (3,)
-        return backend.field_3d_derivs(point, self.z, self.magnetostatic_coeffs)
-     
-    def magnetostatic_potential_at_point(self, point):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\)) close to the axis.
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        assert point.shape == (3,)
-        return backend.potential_3d_derivs(point, self.z, self.magnetostatic_coeffs)
-
-

Ancestors

- -

Methods

-
-
-def electrostatic_field_at_point(self, point) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the x, y and z directions.

-
-
-def electrostatic_potential_at_point(self, point) -
-
-

Compute the electrostatic potential.

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the potential.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def magnetostatic_field_at_point(self, point) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi ) close to the axis.

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-class Field3D_BEM -(electrostatic_point_charges=None, magnetostatic_point_charges=None) -
-
-

An electrostatic field resulting from a general 3D geometry. The field is a result of the surface charges as computed by the -solve_direct() function. See the comments in FieldBEM.

-
- -Expand source code - -
class Field3D_BEM(FieldBEM):
-    """An electrostatic field resulting from a general 3D geometry. The field is a result of the surface charges as computed by the
-    `solve_direct` function. See the comments in `FieldBEM`."""
-     
-    def __init__(self, electrostatic_point_charges=None, magnetostatic_point_charges=None):
-        
-        if electrostatic_point_charges is None:
-            electrostatic_point_charges = EffectivePointCharges.empty_3d()
-        if magnetostatic_point_charges is None:
-            magnetostatic_point_charges = EffectivePointCharges.empty_3d()
-         
-        super().__init__(electrostatic_point_charges, magnetostatic_point_charges, EffectivePointCharges.empty_3d())
-        
-        self.symmetry = E.Symmetry.THREE_D
-
-        for eff in [electrostatic_point_charges, magnetostatic_point_charges]:
-            N = len(eff.charges)
-            assert eff.charges.shape == (N,)
-            assert eff.jacobians.shape == (N, backend.N_TRIANGLE_QUAD)
-            assert eff.positions.shape == (N, backend.N_TRIANGLE_QUAD, 3)
-     
-    def electrostatic_field_at_point(self, point):
-        """
-        Compute the electric field, \( \\vec{E} = -\\nabla \phi \)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the x, y and z directions.
-        """
-        assert point.shape == (3,)
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.field_3d(point, charges, jacobians, positions)
-     
-    def electrostatic_potential_at_point(self, point):
-        """
-        Compute the electrostatic potential.
-
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (3,)
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.potential_3d(point, charges, jacobians, positions)
-     
-    def magnetostatic_field_at_point(self, point):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (3,)
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.field_3d(point, charges, jacobians, positions)
-     
-    def magnetostatic_potential_at_point(self, point):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\))
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        assert point.shape == (3,)
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.potential_3d(point, charges, jacobians, positions)
-    
-    
-    def axial_derivative_interpolation(self, zmin, zmax, N=None):
-        """
-        Use a radial series expansion around the optical axis to allow for very fast field
-        evaluations. Constructing the radial series expansion in 3D is much more complicated
-        than the radial symmetric case, but all details have been abstracted away from the user.
-        
-        Parameters
-        ----------
-        zmin : float
-            Location on the optical axis where to start sampling the radial expansion coefficients.
-            
-        zmax : float
-            Location on the optical axis where to stop sampling the radial expansion coefficients. Any field
-            evaluation outside [zmin, zmax] will return a zero field strength.
-        N: int, optional
-            Number of samples to take on the optical axis, if N=None the amount of samples
-            is determined by taking into account the number of elements in the mesh.
-         
-        Returns
-        -------
-        `Field3DAxial` object allowing fast field evaluations.
-
-        """
-        raise NotImplementedError("Axial interpolation in 3D is only supported in Traceon Pro. Please use 'from traceon_pro import *' at the top of the file.")
-    
-    def area_of_element(self, i):
-        jacobians = self.electrostatic_point_charges.jacobians
-        return np.sum(jacobians[i])
-    
-    def get_tracer(self, bounds):
-        return T.Tracer3D_BEM(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def area_of_element(self, i) -
-
-
-
-
-def axial_derivative_interpolation(self, zmin, zmax, N=None) -
-
-

Use a radial series expansion around the optical axis to allow for very fast field -evaluations. Constructing the radial series expansion in 3D is much more complicated -than the radial symmetric case, but all details have been abstracted away from the user.

-

Parameters

-
-
zmin : float
-
Location on the optical axis where to start sampling the radial expansion coefficients.
-
zmax : float
-
Location on the optical axis where to stop sampling the radial expansion coefficients. Any field -evaluation outside [zmin, zmax] will return a zero field strength.
-
N : int, optional
-
Number of samples to take on the optical axis, if N=None the amount of samples -is determined by taking into account the number of elements in the mesh.
-
-

Returns

-

Field3DAxial object allowing fast field evaluations.

-
-
-def electrostatic_field_at_point(self, point) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the x, y and z directions.

-
-
-def electrostatic_potential_at_point(self, point) -
-
-

Compute the electrostatic potential.

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi )

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-class FieldAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-

An electrostatic field resulting from a radial series expansion around the optical axis. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the axial_derivative_interpolation methods. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldAxial(Field):
-    """An electrostatic field resulting from a radial series expansion around the optical axis. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `axial_derivative_interpolation` methods. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        N = len(z)
-        assert z.shape == (N,)
-        assert electrostatic_coeffs is None or len(electrostatic_coeffs)== N-1
-        assert magnetostatic_coeffs is None or len(magnetostatic_coeffs) == N-1
-        assert electrostatic_coeffs is not None or magnetostatic_coeffs is not None
-        
-        assert z[0] < z[-1], "z values in axial interpolation should be ascending"
-         
-        self.z = z
-        self.electrostatic_coeffs = electrostatic_coeffs if electrostatic_coeffs is not None else np.zeros_like(magnetostatic_coeffs)
-        self.magnetostatic_coeffs = magnetostatic_coeffs if magnetostatic_coeffs is not None else np.zeros_like(electrostatic_coeffs)
-        
-        self.has_electrostatic = np.any(self.electrostatic_coeffs != 0.)
-        self.has_magnetostatic = np.any(self.magnetostatic_coeffs != 0.)
-     
-    def is_electrostatic(self):
-        return self.has_electrostatic
-
-    def is_magnetostatic(self):
-        return self.has_magnetostatic
-     
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}, zmin={self.z[0]} mm, zmax={self.z[-1]} mm,\n\tNumber of samples on optical axis: {len(self.z)}>'
-     
-    def __add__(self, other):
-        if isinstance(other, FieldAxial):
-            assert np.array_equal(self.z, other.z), "Cannot add FieldAxial if optical axis sampling is different."
-            assert self.electrostatic_coeffs.shape == other.electrostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            assert self.magnetostatic_coeffs.shape == other.magnetostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            return self.__class__(self.z, self.electrostatic_coeffs+other.electrostatic_coeffs, self.magnetostatic_coeffs + other.magnetostatic_coeffs)
-         
-        return NotImpemented
-    
-    def __sub__(self, other):
-        return self.__add__(-other)
-    
-    def __radd__(self, other):
-        return self.__add__(other)
-     
-    def __mul__(self, other):
-        if isinstance(other, int) or isinstance(other, float):
-            return self.__class__(self.z, other*self.electrostatic_coeffs, other*self.magnetostatic_coeffs)
-         
-        return NotImpemented
-    
-    def __neg__(self):
-        return -1*self
-    
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-

Inherited members

- -
-
-class FieldBEM -(electrostatic_point_charges, magnetostatic_point_charges, current_point_charges) -
-
-

An electrostatic field (resulting from surface charges) as computed from the Boundary Element Method. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the solve_direct() function. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldBEM(Field):
-    """An electrostatic field (resulting from surface charges) as computed from the Boundary Element Method. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `solve_direct` function. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, electrostatic_point_charges, magnetostatic_point_charges, current_point_charges):
-        assert all([isinstance(eff, EffectivePointCharges) for eff in [electrostatic_point_charges,
-                                                                       magnetostatic_point_charges,
-                                                                       current_point_charges]])
-        self.electrostatic_point_charges = electrostatic_point_charges
-        self.magnetostatic_point_charges = magnetostatic_point_charges
-        self.current_point_charges = current_point_charges
-        self.field_bounds = None
-     
-    def set_bounds(self, bounds):
-        """Set the field bounds. Outside the field bounds the field always returns zero (i.e. no field). Note
-        that even in 2D the field bounds needs to be specified for x,y and z axis. The trajectories in the presence
-        of magnetostatic field are in general 3D even in radial symmetric geometries.
-        
-        Parameters
-        -------------------
-        bounds: (3, 2) np.ndarray of float64
-            The min, max value of x, y, z respectively within the field is still computed.
-        """
-        self.field_bounds = np.array(bounds)
-        assert self.field_bounds.shape == (3,2)
-    
-    def is_electrostatic(self):
-        return len(self.electrostatic_point_charges) > 0
-
-    def is_magnetostatic(self):
-        return len(self.magnetostatic_point_charges) > 0 or len(self.current_point_charges) > 0 
-     
-    def __add__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__add__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__add__(other.magnetostatic_point_charges),
-            self.current_point_charges.__add__(other.current_point_charges))
-     
-    def __sub__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__sub__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__sub__(other.magnetostatic_point_charges),
-            self.current_point_charges.__sub__(other.current_point_charges))
-    
-    def __radd__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__radd__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__radd__(other.magnetostatic_point_charges),
-            self.current_point_charges.__radd__(other.current_point_charges))
-    
-    def __mul__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__mul__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__mul__(other.magnetostatic_point_charges),
-            self.current_point_charges.__mul__(other.current_point_charges))
-    
-    def __neg__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__neg__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__neg__(other.magnetostatic_point_charges),
-            self.current_point_charges.__neg__(other.current_point_charges))
-     
-    def __rmul__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__rmul__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__rmul__(other.magnetostatic_point_charges),
-            self.current_point_charges.__rmul__(other.current_point_charges))
-     
-    def area_of_elements(self, indices):
-        """Compute the total area of the elements at the given indices.
-        
-        Parameters
-        ------------
-        indices: int iterable
-            Indices giving which elements to include in the area calculation.
-
-        Returns
-        ---------------
-        The sum of the area of all elements with the given indices.
-        """
-        return sum(self.area_on_element(i) for i in indices) 
-    
-    def charge_on_element(self, i):
-        return self.area_of_element(i) * self.electrostatic_point_charges.charges[i]
-    
-    def charge_on_elements(self, indices):
-        """Compute the sum of the charges present on the elements with the given indices. To
-        get the total charge of a physical group use `names['name']` for indices where `names` 
-        is returned by `traceon.excitation.Excitation.get_electrostatic_active_elements()`.
-
-        Parameters
-        ----------
-        indices: (N,) array of int
-            indices of the elements contributing to the charge sum. 
-         
-        Returns
-        -------
-        The sum of the charge. See the note about units on the front page."""
-        return sum(self.charge_on_element(i) for i in indices)
-    
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}\n' \
-            f'\tNumber of electrostatic points: {len(self.electrostatic_point_charges)}\n' \
-            f'\tNumber of magnetizable points: {len(self.magnetostatic_point_charges)}\n' \
-            f'\tNumber of current rings: {len(self.current_point_charges)}>'
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def area_of_elements(self, indices) -
-
-

Compute the total area of the elements at the given indices.

-

Parameters

-
-
indices : int iterable
-
Indices giving which elements to include in the area calculation.
-
-

Returns

-

The sum of the area of all elements with the given indices.

-
-
-def charge_on_element(self, i) -
-
-
-
-
-def charge_on_elements(self, indices) -
-
-

Compute the sum of the charges present on the elements with the given indices. To -get the total charge of a physical group use names['name'] for indices where names -is returned by Excitation.get_electrostatic_active_elements().

-

Parameters

-
-
indices : (N,) array of int
-
indices of the elements contributing to the charge sum.
-
-

Returns

-

The sum of the charge. See the note about units on the front page.

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-def set_bounds(self, bounds) -
-
-

Set the field bounds. Outside the field bounds the field always returns zero (i.e. no field). Note -that even in 2D the field bounds needs to be specified for x,y and z axis. The trajectories in the presence -of magnetostatic field are in general 3D even in radial symmetric geometries.

-

Parameters

-
-
bounds : (3, 2) np.ndarray of float64
-
The min, max value of x, y, z respectively within the field is still computed.
-
-
-
-

Inherited members

- -
-
-class FieldRadialAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-
-
- -Expand source code - -
class FieldRadialAxial(FieldAxial):
-    """ """
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        super().__init__(z, electrostatic_coeffs, magnetostatic_coeffs)
-        assert self.electrostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        assert self.magnetostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        self.symmetry = E.Symmetry.RADIAL
-    
-    def electrostatic_field_at_point(self, point):
-        """
-        Compute the electric field, \( \\vec{E} = -\\nabla \phi \)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the r and z directions.
-        """
-        assert point.shape == (2,)
-        return backend.field_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_field_at_point(self, point):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,)
-        return backend.field_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-     
-    def electrostatic_potential_at_point(self, point):
-        """
-        Compute the electrostatic potential (close to the axis).
-
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the potential.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,)
-        return backend.potential_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_potential_at_point(self, point):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\)) close to the axis
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        assert point.shape == (2,)
-        return backend.potential_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialAxial(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def electrostatic_field_at_point(self, point) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the r and z directions.

-
-
-def electrostatic_potential_at_point(self, point) -
-
-

Compute the electrostatic potential (close to the axis).

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the potential.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi ) close to the axis

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-class FieldRadialBEM -(electrostatic_point_charges=None, magnetostatic_point_charges=None, current_point_charges=None) -
-
-

A radially symmetric electrostatic field. The field is a result of the surface charges as computed by the -solve_direct() function. See the comments in FieldBEM.

-
- -Expand source code - -
class FieldRadialBEM(FieldBEM):
-    """A radially symmetric electrostatic field. The field is a result of the surface charges as computed by the
-    `solve_direct` function. See the comments in `FieldBEM`."""
-    
-    def __init__(self, electrostatic_point_charges=None, magnetostatic_point_charges=None, current_point_charges=None):
-        if electrostatic_point_charges is None:
-            electrostatic_point_charges = EffectivePointCharges.empty_2d()
-        if magnetostatic_point_charges is None:
-            magnetostatic_point_charges = EffectivePointCharges.empty_2d()
-        if current_point_charges is None:
-            current_point_charges = EffectivePointCharges.empty_3d()
-         
-        self.symmetry = E.Symmetry.RADIAL
-        super().__init__(electrostatic_point_charges, magnetostatic_point_charges, current_point_charges)
-         
-    def current_field_at_point(self, point):
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-         
-        if point.shape == (2,):
-            # Input point is 2D, so return a 2D point
-            result = backend.current_field(backend._vec_2d_to_3d(point), currents, jacobians, positions)
-            assert np.isclose(result[1], 0.)
-            return backend._vec_3d_to_2d(result)
-        else:
-            return backend.current_field(point, currents, jacobians, positions)
-     
-    def electrostatic_field_at_point(self, point):
-        """
-        Compute the electric field, \( \\vec{E} = -\\nabla \phi \)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the r and z directions.   
-        """
-        assert point.shape == (2,) or point.shape == (3,)
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.field_radial(point, charges, jacobians, positions)
-     
-    def electrostatic_potential_at_point(self, point):
-        """
-        Compute the electrostatic potential.
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,) or point.shape == (3,)
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.potential_radial(point, charges, jacobians, positions)
-    
-    def magnetostatic_field_at_point(self, point):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point).astype(np.float64)
-        assert point.shape == (2,)
-        current_field = self.current_field_at_point(point)
-        
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        
-        mag_field = backend.field_radial(point, charges, jacobians, positions)
-
-        return current_field + mag_field
-
-    def magnetostatic_potential_at_point(self, point):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\))
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-
-        assert point.shape == (2,)
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.potential_radial(point, charges, jacobians, positions)
-    
-    def current_potential_axial(self, z):
-        assert isinstance(z, float)
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-        return backend.current_potential_axial(z, currents, jacobians, positions)
-     
-    def get_electrostatic_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the electrostatic potential a points on the optical axis (z-axis). 
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.axial_derivatives_radial(z, charges, jacobians, positions)
-    
-    def get_magnetostatic_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the magnetostatic potential at points on the optical axis (z-axis). 
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-         
-        derivs_magnetic = backend.axial_derivatives_radial(z, charges, jacobians, positions)
-        derivs_current = self.get_current_axial_potential_derivatives(z)
-        return derivs_magnetic + derivs_current
-     
-    def get_current_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the current magnetostatic scalar potential at points on the optical axis.
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-         
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-        return backend.current_axial_derivatives_radial(z, currents, jacobians, positions)
-      
-    def area_of_element(self, i):
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return 2*np.pi*np.sum(jacobians[i] * positions[i, :, 0])
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialBEM(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def area_of_element(self, i) -
-
-
-
-
-def current_field_at_point(self, point) -
-
-
-
-
-def current_potential_axial(self, z) -
-
-
-
-
-def electrostatic_field_at_point(self, point) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the r and z directions.

-
-
-def electrostatic_potential_at_point(self, point) -
-
-

Compute the electrostatic potential.

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_current_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the current magnetostatic scalar potential at points on the optical axis.

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_electrostatic_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the electrostatic potential a points on the optical axis (z-axis).

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_magnetostatic_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the magnetostatic potential at points on the optical axis (z-axis).

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi )

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc1/tracing.html b/docs/docs/v0.7.0rc1/tracing.html deleted file mode 100644 index 1a3c6e9c..00000000 --- a/docs/docs/v0.7.0rc1/tracing.html +++ /dev/null @@ -1,522 +0,0 @@ - - - - - - -traceon.tracing API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.tracing

-
-
-

The tracing module allows to trace electrons within any field type returned by the traceon.solver module. The tracing algorithm -used is RK45 with adaptive step size control [1]. The tracing code is implemented in C (see traceon.backend) and has therefore -excellent performance. The module also provides various helper functions to define appropriate initial velocity vectors and to -compute intersections of the computed traces with various planes.

-

References

-

[1] Erwin Fehlberg. Low-Order Classical Runge-Kutta Formulas With Stepsize Control and their Application to Some Heat -Transfer Problems. 1969. National Aeronautics and Space Administration.

-
-
-
-
-
-
-

Functions

-
-
-def axis_intersection(positions) -
-
-

Calculate the intersection with the optical axis using a linear interpolation. Notice that -this only makes sense in 2D as in 3D the particle will never pass exactly through the optical axis. -However, this function is implemented as yz_plane_intersection()(positions, 0.0) and will therefore -give meaningful results in 3D if you expect the particle trajectory to be in the xz plane. This function -only returns the z-coordinate. Use yz_plane_intersection() directly if you want to retrieve the velocity -components.

-

Parameters

-
-
positions : (N, 4) or (N, 6) np.ndarray of float64
-
positions of an electron as returned by Tracer.
-
-

Returns

-
-
float z-coordinate of intersection point
-
 
-
-
-
-def line_intersection(positions, p0, tangent) -
-
-

Compute the intersection of a trajectory with a line in 2D. The line is specified -by a point (p0) on the line and a vector tangential (tangent) to the line. The intersection -point is calculated using a linear interpolation.

-

Parameters

-
-
positions : (N, 4) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
p0 : (2,) np.ndarray of float64
-
A point that lies on the line.
-
tangent : (2,) np.ndarray of float64
-
A vector that is tangential to the line. A point p lies on the line if there exists a number k -such that p0 + k*tangent = p.
-
-

Returns

-

np.ndarray of shape (4,) containing the position and velocity of the electron at the intersection point.

-
-
-def plane_intersection(positions, p0, normal) -
-
-

Compute the intersection of a trajectory with a general plane in 3D. The plane is specified -by a point (p0) in the plane and a normal vector (normal) to the plane. The intersection -point is calculated using a linear interpolation.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
p0 : (3,) np.ndarray of float64
-
A point that lies in the plane.
-
normal : (3,) np.ndarray of float64
-
A vector that is normal to the plane. A point p lies in the plane iff dot(normal, p - p0) = 0 where -dot is the dot product.
-
-

Returns

-

np.ndarray of shape (6,) containing the position and velocity of the electron at the intersection point.

-
-
-def velocity_vec(eV, direction) -
-
-

Compute an initial velocity vector in the correct units and direction.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
direction : (2,) or (3,) numpy array
-
vector giving the correct direction of the initial velocity vector. Does not -have to be a unit vector as it is always normalized.
-
-

Returns

-

Initial velocity vector with magnitude corresponding to the supplied energy (in eV). -The shape of the resulting vector is the same as the shape of direction.

-
-
-def velocity_vec_spherical(eV, theta, phi) -
-
-

Compute initial velocity vector given energy and direction computed from spherical coordinates.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
theta : float
-
angle with z-axis (same definition as theta in a spherical coordinate system)
-
phi : float
-
angle with the x-axis (same definition as phi in a spherical coordinate system)
-
-

Returns

-

Initial velocity vector of shape (3,) with magnitude corresponding to the supplied energy (in eV).

-
-
-def velocity_vec_xz_plane(eV, angle, downward=True, three_dimensional=False) -
-
-

Compute initial velocity vector in the xz plane with the given energy and angle with z-axis.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
angle : float
-
angle with z-axis
-
downward : bool
-
whether the velocity vector should point upward or downwards
-
three_dimensional : bool
-
whether the resulting velocity vector has shape (2,) or shape (3,)
-
-

Returns

-

Initial velocity vector with magnitude corresponding to the supplied energy (in eV).

-
-
-def xy_plane_intersection(positions, z) -
-
-

Compute the intersection of a trajectory with an xy-plane.

-

Parameters

-
-
positions : (N, 4) or (N, 6) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
z : float
-
z-coordinate of the plane with which to compute the intersection
-
-

Returns

-

np.ndarray of shape (4,) or (6,) containing the position and velocity of the electron at the intersection point.

-
-
-def xz_plane_intersection(positions, y) -
-
-

Compute the intersection of a trajectory with an xz-plane. Note that this function -does not make sense in 2D (where we consider (r,z) as (x,z) and therefore the y-axis is missing).

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
y : float
-
y-coordinate of the plane with which to compute the intersection
-
-

Returns

-

np.ndarray of shape (6,) containing the position and velocity of the electron at the intersection point.

-
-
-def yz_plane_intersection(positions, x) -
-
-

Compute the intersection of a trajectory with an yz-plane.

-

Parameters

-
-
positions : (N, 4) or (N, 6) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
x : float
-
x-coordinate of the plane with which to compute the intersection
-
-

Returns

-

np.ndarray of shape (4,) or (6,) containing the position and velocity of the electron at the intersection point.

-
-
-
-
-

Classes

-
-
-class Tracer -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer:
-    """General electron tracer class. Can trace electrons given any field class from `traceon.solver`.
-
-    Parameters
-    ----------
-    field: traceon.solver.Field (or any class inheriting Field)
-        The field used to compute the force felt by the electron.
-    bounds: (3, 2) np.ndarray of float64
-        Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-    """
-    
-    def __init__(self, field, bounds):
-        self.field = field
-         
-        bounds = np.array(bounds).astype(np.float64)
-        assert bounds.shape == (3,2)
-        self.bounds = bounds
-    
-    def __str__(self):
-        field_name = self.field.__class__.__name__
-        bounds_str = ' '.join([f'({bmin:.2f}, {bmax:.2f})' for bmin, bmax in self.bounds])
-        return f'<Traceon Tracer of {field_name},\n\t' \
-            + 'Bounds: ' + bounds_str + ' mm >'
-    
-    def __call__(self, position, velocity):
-        """Trace an electron.
-
-        Parameters
-        ----------
-        position: (2,) or (3,) np.ndarray of float64
-            Initial position of electron.
-        velocity: (2,) or (3,) np.ndarray of float64
-            Initial velocity (expressed in a vector whose magnitude has units of eV). Use one of the utility functions documented
-            above to create the initial velocity vector.
-        atol: float
-            Absolute tolerance determining the accuracy of the trace.
-        
-        Returns
-        -------
-        `(times, positions)` which is a tuple of two numpy arrays. `times` is one dimensional and contains the times
-        (in ns) at which the positions have been computed. The `positions` array is two dimensional, `positions[i]` correspond
-        to time step `times[i]`. One element of the positions array has shape (6,).
-        The first three elements in the `positions[i]` array contain the x,y,z positions.
-        The last three elements in `positions[i]` contain the vx,vy,vz velocities.
-        """
-        raise RuntimeError('Please use the field.get_tracer(...) method to get the appropriate Tracer instance')
-
-

Subclasses

- -

Methods

-
-
-def __call__(self, position, velocity) -
-
-

Trace an electron.

-

Parameters

-
-
position : (2,) or (3,) np.ndarray of float64
-
Initial position of electron.
-
velocity : (2,) or (3,) np.ndarray of float64
-
Initial velocity (expressed in a vector whose magnitude has units of eV). Use one of the utility functions documented -above to create the initial velocity vector.
-
atol : float
-
Absolute tolerance determining the accuracy of the trace.
-
-

Returns

-

(times, positions) which is a tuple of two numpy arrays. times is one dimensional and contains the times -(in ns) at which the positions have been computed. The positions array is two dimensional, positions[i] correspond -to time step times[i]. One element of the positions array has shape (6,). -The first three elements in the positions[i] array contain the x,y,z positions. -The last three elements in positions[i] contain the vx,vy,vz velocities.

-
-
-
-
-class Tracer3DAxial -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer3DAxial(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        velocity = _convert_velocity_to_SI(velocity)
-        return backend.trace_particle_3d_derivs(position, velocity, self.bounds, atol,
-            self.field.z, self.field.electrostatic_coeffs, self.field.magnetostatic_coeffs)
-
-

Ancestors

- -

Inherited members

- -
-
-class Tracer3D_BEM -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer3D_BEM(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        velocity = _convert_velocity_to_SI(velocity)
-        bounds = self.field.field_bounds
-        elec, mag = self.field.electrostatic_point_charges, self.field.magnetostatic_point_charges
-        return backend.trace_particle_3d(position, velocity, self.bounds, atol, elec, mag)
-
-

Ancestors

- -

Inherited members

- -
-
-class TracerRadialAxial -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class TracerRadialAxial(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-
-        velocity = _convert_velocity_to_SI(velocity)
-        
-        elec, mag = self.field.electrostatic_coeffs, self.field.magnetostatic_coeffs
-        
-        return backend.trace_particle_radial_derivs(position, velocity, self.bounds, atol, self.field.z, elec, mag)
-
-

Ancestors

- -

Inherited members

- -
-
-class TracerRadialBEM -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class TracerRadialBEM(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        
-        velocity = _convert_velocity_to_SI(velocity)
-        
-        return backend.trace_particle_radial(
-                position,
-                velocity, 
-                self.bounds,
-                atol, 
-                self.field.electrostatic_point_charges,
-                self.field.magnetostatic_point_charges,
-                self.field.current_point_charges,
-                field_bounds=self.field.field_bounds)
-
-

Ancestors

- -

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc2/geometry.html b/docs/docs/v0.7.0rc2/geometry.html deleted file mode 100644 index bd04c26b..00000000 --- a/docs/docs/v0.7.0rc2/geometry.html +++ /dev/null @@ -1,2145 +0,0 @@ - - - - - - -traceon.geometry API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.geometry

-
-
-

The geometry module allows the creation of general meshes in 2D and 3D. -The builtin mesher uses so called parametric meshes, meaning -that for any mesh we construct a mathematical formula mapping to points on the mesh. This makes it -easy to generate structured (or transfinite) meshes. These meshes usually help the mesh to converge -to the right answer faster, since the symmetries of the mesh (radial, multipole, etc.) are better -represented.

-

The parametric mesher also has downsides, since it's for example harder to generate meshes with -lots of holes in them (the 'cut' operation is not supported). For these cases, Traceon makes it easy to import -meshes generated by other programs (e.g. GMSH or Comsol). Traceon can import meshio meshes -or any file format supported by meshio.

-
-
-
-
-
-
-
-
-

Classes

-
-
-class Path -(fun, path_length, breakpoints=[], name=None) -
-
-

A path is a mapping from a number in the range [0, path_length] to a three dimensional point. Note that Path is a -subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Path(GeometricObject):
-    """A path is a mapping from a number in the range [0, path_length] to a three dimensional point. Note that `Path` is a
-    subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-    
-    def __init__(self, fun, path_length, breakpoints=[], name=None):
-        # Assumption: fun takes in p, the path length
-        # and returns the point on the path
-        self.fun = fun
-        self.path_length = path_length
-        assert self.path_length > 0
-        self.breakpoints = breakpoints
-        self.name = name
-    
-    @staticmethod
-    def from_irregular_function(to_point, N=100, breakpoints=[]):
-        """Construct a path from a function that is of the form u -> point, where 0 <= u <= 1.
-        The length of the path is determined by integration.
-
-        Parameters
-        ---------------------------------
-        to_point: callable
-            A function accepting a number in the range [0, 1] and returns a the dimensional point.
-        N: int
-            Number of samples to use in the cubic spline interpolation.
-        breakpoints: float iterable
-            Points (0 <= u <= 1) on the path where the function is non-differentiable. These points
-            are always included in the resulting mesh.
-
-        Returns
-        ---------------------------------
-        Path"""
-         
-        # path length = integrate |f'(x)|
-        fun = lambda u: np.array(to_point(u))
-        
-        u = np.linspace(0, 1, N)
-        samples = CubicSpline(u, [fun(u_) for u_ in u])
-        derivatives = samples.derivative()(u)
-        norm_derivatives = np.linalg.norm(derivatives, axis=1)
-        path_lengths = CubicSpline(u, norm_derivatives).antiderivative()(u)
-        interpolation = CubicSpline(path_lengths, u) # Path length to [0,1]
-
-        path_length = path_lengths[-1]
-        
-        return Path(lambda pl: fun(interpolation(pl)), path_length, breakpoints=[b*path_length for b in breakpoints])
-    
-    @staticmethod
-    def spline_through_points(points, N=100):
-        """Construct a path by fitting a cubic spline through the given points.
-
-        Parameters
-        -------------------------
-        points: (N, 3) ndarray of float
-            Three dimensional points through which the spline is fitted.
-
-        Returns
-        -------------------------
-        Path"""
-
-        x = np.linspace(0, 1, len(points))
-        interp = CubicSpline(x, points)
-        return Path.from_irregular_function(interp, N=N)
-     
-    def average(self, fun):
-        """Average a function along the path, by integrating 1/l * fun(path(l)) with 0 <= l <= path length.
-
-        Parameters
-        --------------------------
-        fun: callable (3,) -> float
-            A function taking a three dimensional point and returning a float.
-
-        Returns
-        -------------------------
-        float
-
-        The average value of the function along the point."""
-        return quad(lambda s: fun(self(s)), 0, self.path_length, points=self.breakpoints)[0]/self.path_length
-     
-    def map_points(self, fun):
-        """Return a new function by mapping a function over points along the path (see `traceon.mesher.GeometricObject`).
-        The path length is assumed to stay the same after this operation.
-        
-        Parameters
-        ----------------------------
-        fun: callable (3,) -> (3,)
-            Function taking three dimensional points and returning three dimensional points.
-
-        Returns
-        ---------------------------
-        Path"""
-        return Path(lambda u: fun(self(u)), self.path_length, self.breakpoints, name=self.name)
-     
-    def __call__(self, t):
-        """Evaluate a point along the path.
-
-        Parameters
-        ------------------------
-        t: float
-            The length along the path.
-
-        Returns
-        ------------------------
-        (3,) float
-
-        Three dimensional point."""
-        return self.fun(t)
-     
-    def is_closed(self):
-        """Determine whether the path is closed, by comparing the starting and endpoint.
-
-        Returns
-        ----------------------
-        bool: True if the path is closed, False otherwise."""
-        return _points_close(self.starting_point(), self.endpoint())
-    
-    def add_phase(self, l):
-        """Add a phase to a closed path. A path is closed when the starting point is equal to the
-        end point. A phase of length l means that the path starts 'further down' the closed path.
-
-        Parameters
-        --------------------
-        l: float
-            The phase (expressed as a path length). The resulting path starts l distance along the 
-            original path.
-
-        Returns
-        --------------------
-        Path"""
-        assert self.is_closed()
-        
-        def fun(u):
-            return self( (l + u) % self.path_length )
-        
-        return Path(fun, self.path_length, sorted([(b-l)%self.path_length for b in self.breakpoints + [0.]]), name=self.name)
-     
-    def __rshift__(self, other):
-        """Combine two paths to create a single path. The endpoint of the first path needs
-        to match the starting point of the second path. This common point is marked as a breakpoint and
-        always included in the mesh. To use this function use the right shift operator (p1 >> p2).
-
-        Parameters
-        -----------------------
-        other: Path
-            The second path, to extend the current path.
-
-        Returns
-        -----------------------
-        Path"""
-
-        assert isinstance(other, Path), "Exteding path with object that is not actually a Path"
-
-        assert _points_close(self.endpoint(), other.starting_point())
-
-        total = self.path_length + other.path_length
-         
-        def f(t):
-            assert 0 <= t <= total
-            
-            if t <= self.path_length:
-                return self(t)
-            else:
-                return other(t - self.path_length)
-        
-        return Path(f, total, self.breakpoints + [self.path_length] + other.breakpoints, name=self.name)
-
-    def starting_point(self):
-        """Returns the starting point of the path.
-
-        Returns
-        ---------------------
-        (3,) float
-
-        The starting point of the path."""
-        return self(0.)
-    
-    def middle_point(self):
-        """Returns the midpoint of the path (in terms of length along the path.)
-
-        Returns
-        ----------------------
-        (3,) float
-        
-        The point at the middle of the path."""
-        return self(self.path_length/2)
-    
-    def endpoint(self):
-        """Returns the endpoint of the path.
-
-        Returns
-        ------------------------
-        (3,) float
-        
-        The endpoint of the path."""
-        return self(self.path_length)
-    
-    def line_to(self, point):
-        """Extend the current path by a line from the current endpoint to the given point.
-        The given point is marked a breakpoint.
-
-        Parameters
-        ----------------------
-        point: (3,) float
-            The new endpoint.
-
-        Returns
-        ---------------------
-        Path"""
-        point = np.array(point)
-        assert point.shape == (3,), "Please supply a three dimensional point to .line_to(...)"
-        l = Path.line(self.endpoint(), point)
-        return self >> l
-     
-    @staticmethod
-    def circle_xz(x0, z0, radius, angle=2*pi):
-        """Returns (part of) a circle in the XZ plane around the x-axis. Starting on the positive x-axis.
-        
-        Parameters
-        --------------------------------
-        x0: float
-            x-coordinate of the center of the circle
-        z0: float
-            z-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([radius*cos(theta), 0., radius*sin(theta)])
-        return Path(f, angle*radius).move(dx=x0, dz=z0)
-    
-    @staticmethod
-    def circle_yz(y0, z0, radius, angle=2*pi):
-        """Returns (part of) a circle in the YZ plane around the x-axis. Starting on the positive y-axis.
-        
-        Parameters
-        --------------------------------
-        y0: float
-            x-coordinate of the center of the circle
-        z0: float
-            z-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([0., radius*cos(theta), radius*sin(theta)])
-        return Path(f, angle*radius).move(dy=y0, dz=z0)
-    
-    @staticmethod
-    def circle_xy(x0, y0, radius, angle=2*pi):
-        """Returns (part of) a circle in the XY plane around the z-axis. Starting on the positive X-axis.
-        
-        Parameters
-        --------------------------------
-        y0: float
-            x-coordinate of the center of the circle
-        y0: float
-            y-coordiante of the center of the circle
-        radius: float
-            radius of the circle
-        angle: float
-            The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-        Returns
-        ---------------------------------
-        Path"""
-        def f(u):
-            theta = u / radius 
-            return np.array([radius*cos(theta), radius*sin(theta), 0.])
-        return Path(f, angle*radius).move(dx=x0, dy=y0)
-     
-    def arc_to(self, center, end, reverse=False):
-        """Extend the current path using an arc.
-
-        Parameters
-        ----------------------------
-        center: (3,) float
-            The center point of the arc.
-        end: (3,) float
-            The endpoint of the arc, shoud lie on a circle determined
-            by the given centerpoint and the current endpoint.
-
-        Returns
-        -----------------------------
-        Path"""
-        start = self.endpoint()
-        return self >> Path.arc(center, start, end, reverse=reverse)
-    
-    @staticmethod
-    def arc(center, start, end, reverse=False):
-        """Return an arc by specifying the center, start and end point.
-
-        Parameters
-        ----------------------------
-        center: (3,) float
-            The center point of the arc.
-        start: (3,) float
-            The start point of the arc.
-        end: (3,) float
-            The endpoint of the arc.
-
-        Returns
-        ----------------------------
-        Path"""
-        start_arr, center_arr, end_arr = np.array(start), np.array(center), np.array(end)
-         
-        x_unit = start_arr - center_arr
-        x_unit /= np.linalg.norm(x_unit)
-
-        vector = end_arr - center_arr
-         
-        y_unit = vector - np.dot(vector, x_unit) * x_unit
-        y_unit /= np.linalg.norm(y_unit)
-
-        radius = np.linalg.norm(start_arr - center_arr) 
-        theta_max = atan2(np.dot(vector, y_unit), np.dot(vector, x_unit))
-
-        if reverse:
-            theta_max = theta_max - 2*pi
-
-        path_length = abs(theta_max * radius)
-          
-        def f(l):
-            theta = l/path_length * theta_max
-            return center + radius*cos(theta)*x_unit + radius*sin(theta)*y_unit
-        
-        return Path(f, path_length)
-     
-    def revolve_x(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the x-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-        
-        pstart, pmiddle, pend = self.starting_point(), self.middle_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[1]**2 + p[2]**2))
-        length2 = 2*pi*r_avg
-         
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[2], p[1])
-            r = sqrt(p[1]**2 + p[2]**2)
-            return np.array([p[0], r*cos(theta + v/length2*angle), r*sin(theta + v/length2*angle)])
-         
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-    
-    def revolve_y(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the y-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-
-        pstart, pend = self.starting_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[0]**2 + p[2]**2))
-        length2 = 2*pi*r_avg
-         
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[2], p[0])
-            r = sqrt(p[0]*p[0] + p[2]*p[2])
-            return np.array([r*cos(theta + v/length2*angle), p[1], r*sin(theta + v/length2*angle)])
-         
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-    
-    def revolve_z(self, angle=2*pi):
-        """Create a surface by revolving the path anti-clockwise around the z-axis.
-        
-        Parameters
-        -----------------------
-        angle: float
-            The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-        Returns
-        -----------------------
-        Surface"""
-
-        pstart, pend = self.starting_point(), self.endpoint()
-        r_avg = self.average(lambda p: sqrt(p[0]**2 + p[1]**2))
-        length2 = 2*pi*r_avg
-        
-        def f(u, v):
-            p = self(u)
-            theta = atan2(p[1], p[0])
-            r = sqrt(p[0]*p[0] + p[1]*p[1])
-            return np.array([r*cos(theta + v/length2*angle), r*sin(theta + v/length2*angle), p[2]])
-        
-        return Surface(f, self.path_length, length2, self.breakpoints, name=self.name)
-     
-    def extrude(self, vector):
-        """Create a surface by extruding the path along a vector. The vector gives both
-        the length and the direction of the extrusion.
-
-        Parameters
-        -------------------------
-        vector: (3,) float
-            The direction and length (norm of the vector) to extrude by.
-
-        Returns
-        -------------------------
-        Surface"""
-        vector = np.array(vector)
-        length = np.linalg.norm(vector)
-         
-        def f(u, v):
-            return self(u) + v/length*vector
-        
-        return Surface(f, self.path_length, length, self.breakpoints, name=self.name)
-    
-    def extrude_by_path(self, p2):
-        """Create a surface by extruding the path along a second path. The second
-        path does not need to start along the first path. Imagine the surface created
-        by moving the first path along the second path.
-
-        Parameters
-        -------------------------
-        p2: Path
-            The (second) path defining the extrusion.
-
-        Returns
-        ------------------------
-        Surface"""
-        p0 = p2.starting_point()
-         
-        def f(u, v):
-            return self(u) + p2(v) - p0
-
-        return Surface(f, self.path_length, p2.path_length, self.breakpoints, p2.breakpoints, name=self.name)
-
-    def close(self):
-        """Close the path, by making a straight line to the starting point.
-
-        Returns
-        -------------------
-        Path"""
-        return self.line_to(self.starting_point())
-    
-    @staticmethod
-    def ellipse(major, minor):
-        """Create a path along the outline of an ellipse. The ellipse lies
-        in the XY plane, and the path starts on the positive x-axis.
-
-        Parameters
-        ---------------------------
-        major: float
-            The major axis of the ellipse (lies along the x-axis).
-        minor: float
-            The minor axis of the ellipse (lies along the y-axis).
-
-        Returns
-        ---------------------------
-        Path"""
-        # Crazy enough there is no closed formula
-        # to go from path length to a point on the ellipse.
-        # So we have to use `from_irregular_function`
-        def f(u):
-            return np.array([major*cos(2*pi*u), minor*sin(2*pi*u), 0.])
-        return Path.from_irregular_function(f)
-    
-    @staticmethod
-    def line(from_, to):
-        """Create a straight line between two points.
-
-        Parameters
-        ------------------------------
-        from_: (3,) float
-            The starting point of the path.
-        to: (3,) float
-            The endpoint of the path.
-
-        Returns
-        ---------------------------
-        Path"""
-        from_, to = np.array(from_), np.array(to)
-        length = np.linalg.norm(from_ - to)
-        return Path(lambda pl: (1-pl/length)*from_ + pl/length*to, length)
-
-    def cut(self, length):
-        """Cut the path in two at a specific length along the path.
-
-        Parameters
-        --------------------------------------
-        length: float
-            The length along the path at which to cut.
-
-        Returns
-        -------------------------------------
-        (Path, Path)
-        
-        A tuple containing two paths. The first path contains the path upto length, while the second path contains the rest."""
-        return (Path(self.fun, length, [b for b in self.breakpoints if b <= length], name=self.name),
-                Path(lambda l: self.fun(l + length), self.path_length - length, [b - length for b in self.breakpoints if b >= length], name=self.name))
-    
-    @staticmethod
-    def rectangle_xz(xmin, xmax, zmin, zmax):
-        """Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is 
-        counter clockwise around the y-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-        return Path.line([xmin, 0., zmin], [xmax, 0, zmin]) \
-            .line_to([xmax, 0, zmax]).line_to([xmin, 0., zmax]).close()
-     
-    @staticmethod
-    def rectangle_yz(ymin, ymax, zmin, zmax):
-        """Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is 
-        counter clockwise around the x-axis.
-        
-        Parameters
-        ------------------------
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-
-        return Path.line([0., ymin, zmin], [0, ymin, zmax]) \
-            .line_to([0., ymax, zmax]).line_to([0., ymax, zmin]).close()
-     
-    @staticmethod
-    def rectangle_xy(xmin, xmax, ymin, ymax):
-        """Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is 
-        counter clockwise around the z-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Path"""
-        return Path.line([xmin, ymin, 0.], [xmin, ymax, 0.]) \
-            .line_to([xmax, ymax, 0.]).line_to([xmax, ymin, 0.]).close()
-    
-    @staticmethod
-    def aperture(height, radius, extent, z=0.):
-        """Create an 'aperture'. Note that in a radially symmetric geometry
-        an aperture is basically a rectangle with the right side 'open'. Revolving
-        this path around the z-axis would generate a cylindircal hole in the center. 
-        This is the most basic model of an aperture.
-
-        Parameters
-        ------------------------
-        height: float
-            The height of the aperture
-        radius: float
-            The radius of the aperture hole (distance to the z-axis)
-        extent: float
-            The maximum x value
-        z: float
-            The z-coordinate of the center of the aperture
-
-        Returns
-        ------------------------
-        Path"""
-        return Path.line([extent, 0., -height/2], [radius, 0., -height/2])\
-                .line_to([radius, 0., height/2]).line_to([extent, 0., height/2]).move(dz=z)
-    
-    def __add__(self, other):
-        """Add two paths to create a PathCollection. Note that a PathCollection supports
-        a subset of the methods of Path (for example, movement, rotation and meshing). Use
-        the + operator to combine paths into a path collection: path1 + path2 + path3.
-
-        Returns
-        -------------------------
-        PathCollection"""
-         
-        if not isinstance(other, Path) and not isinstance(other, PathCollection):
-            return NotImplemented
-        
-        if isinstance(other, Path):
-            return PathCollection([self, other])
-        elif isinstance(other, PathCollection):
-            return PathCollection([self] + [other.paths])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False, name=None):
-        """Mesh the path, so it can be used in the BEM solver.
-
-        Parameters
-        --------------------------
-        mesh_size: float
-            Determines amount of elements in the mesh. A smaller
-            mesh size leads to more elements.
-        mesh_size_factor: float
-            Alternative way to specify the mesh size, which scales
-            with the dimensions of the geometry, and therefore more
-            easily translates between different geometries.
-        higher_order: bool
-            Whether to generate a higher order mesh. A higher order
-            produces curved line elements (determined by 4 points on
-            each curved element). The BEM solver supports higher order
-            elements in radial symmetric geometries only.
-
-        Returns
-        ----------------------------
-        Path"""
-        u = discretize_path(self.path_length, self.breakpoints, mesh_size, mesh_size_factor, N_factor=3 if higher_order else 1)
-        
-        N = len(u) 
-        points = np.zeros( (N, 3) )
-         
-        for i in range(N):
-            points[i] = self(u[i])
-         
-        if not higher_order:
-            lines = np.array([np.arange(N-1), np.arange(1, N)]).T
-        else:
-            assert N % 3 == 1
-            r = np.arange(N)
-            p0 = r[0:-1:3]
-            p1 = r[3::3]
-            p2 = r[1::3]
-            p3 = r[2::3]
-            lines = np.array([p0, p1, p2, p3]).T
-          
-        assert lines.dtype == np.int64 or lines.dtype == np.int32
-        
-        name = self.name if name is None else name
-         
-        if name is not None:
-            physical_to_lines = {name:np.arange(len(lines))}
-        else:
-            physical_to_lines = {}
-        
-        return Mesh(points=points, lines=lines, physical_to_lines=physical_to_lines)
-
-    def __str__(self):
-        return f"<Path name:{self.name}, length:{self.path_length:.1e}, number of breakpoints:{len(self.breakpoints)}>"
-
-

Ancestors

- -

Static methods

-
-
-def aperture(height, radius, extent, z=0.0) -
-
-

Create an 'aperture'. Note that in a radially symmetric geometry -an aperture is basically a rectangle with the right side 'open'. Revolving -this path around the z-axis would generate a cylindircal hole in the center. -This is the most basic model of an aperture.

-

Parameters

-
-
height : float
-
The height of the aperture
-
radius : float
-
The radius of the aperture hole (distance to the z-axis)
-
extent : float
-
The maximum x value
-
z : float
-
The z-coordinate of the center of the aperture
-
-

Returns

-
-
Path
-
 
-
-
-
-def arc(center, start, end, reverse=False) -
-
-

Return an arc by specifying the center, start and end point.

-

Parameters

-
-
center : (3,) float
-
The center point of the arc.
-
start : (3,) float
-
The start point of the arc.
-
end : (3,) float
-
The endpoint of the arc.
-
-

Returns

-
-
Path
-
 
-
-
-
-def circle_xy(x0, y0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the XY plane around the z-axis. Starting on the positive X-axis.

-

Parameters

-
-
y0 : float
-
x-coordinate of the center of the circle
-
y0 : float
-
y-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def circle_xz(x0, z0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the XZ plane around the x-axis. Starting on the positive x-axis.

-

Parameters

-
-
x0 : float
-
x-coordinate of the center of the circle
-
z0 : float
-
z-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def circle_yz(y0, z0, radius, angle=6.283185307179586) -
-
-

Returns (part of) a circle in the YZ plane around the x-axis. Starting on the positive y-axis.

-

Parameters

-
-
y0 : float
-
x-coordinate of the center of the circle
-
z0 : float
-
z-coordiante of the center of the circle
-
radius : float
-
radius of the circle
-
angle : float
-
The circumference of the circle in radians. The default of 2*pi gives a full circle.
-
-

Returns

-
-
Path
-
 
-
-
-
-def ellipse(major, minor) -
-
-

Create a path along the outline of an ellipse. The ellipse lies -in the XY plane, and the path starts on the positive x-axis.

-

Parameters

-
-
major : float
-
The major axis of the ellipse (lies along the x-axis).
-
minor : float
-
The minor axis of the ellipse (lies along the y-axis).
-
-

Returns

-
-
Path
-
 
-
-
-
-def from_irregular_function(to_point, N=100, breakpoints=[]) -
-
-

Construct a path from a function that is of the form u -> point, where 0 <= u <= 1. -The length of the path is determined by integration.

-

Parameters

-
-
to_point : callable
-
A function accepting a number in the range [0, 1] and returns a the dimensional point.
-
N : int
-
Number of samples to use in the cubic spline interpolation.
-
breakpoints : float iterable
-
Points (0 <= u <= 1) on the path where the function is non-differentiable. These points -are always included in the resulting mesh.
-
-

Returns

-
-
Path
-
 
-
-
-
-def line(from_, to) -
-
-

Create a straight line between two points.

-

Parameters

-
-
from_ : (3,) float
-
The starting point of the path.
-
to : (3,) float
-
The endpoint of the path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def rectangle_xy(xmin, xmax, ymin, ymax) -
-
-

Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is -counter clockwise around the z-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def rectangle_xz(xmin, xmax, zmin, zmax) -
-
-

Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is -counter clockwise around the y-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def rectangle_yz(ymin, ymax, zmin, zmax) -
-
-

Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is -counter clockwise around the x-axis.

-

Parameters

-
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def spline_through_points(points, N=100) -
-
-

Construct a path by fitting a cubic spline through the given points.

-

Parameters

-
-
points : (N, 3) ndarray of float
-
Three dimensional points through which the spline is fitted.
-
-

Returns

-
-
Path
-
 
-
-
-
-

Methods

-
-
-def __call__(self, t) -
-
-

Evaluate a point along the path.

-

Parameters

-
-
t : float
-
The length along the path.
-
-

Returns

-

(3,) float

-

Three dimensional point.

-
-
-def __rshift__(self, other) -
-
-

Combine two paths to create a single path. The endpoint of the first path needs -to match the starting point of the second path. This common point is marked as a breakpoint and -always included in the mesh. To use this function use the right shift operator (p1 >> p2).

-

Parameters

-
-
other : Path
-
The second path, to extend the current path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def add_phase(self, l) -
-
-

Add a phase to a closed path. A path is closed when the starting point is equal to the -end point. A phase of length l means that the path starts 'further down' the closed path.

-

Parameters

-
-
l : float
-
The phase (expressed as a path length). The resulting path starts l distance along the -original path.
-
-

Returns

-
-
Path
-
 
-
-
-
-def arc_to(self, center, end, reverse=False) -
-
-

Extend the current path using an arc.

-

Parameters

-
-
center : (3,) float
-
The center point of the arc.
-
end : (3,) float
-
The endpoint of the arc, shoud lie on a circle determined -by the given centerpoint and the current endpoint.
-
-

Returns

-
-
Path
-
 
-
-
-
-def average(self, fun) -
-
-

Average a function along the path, by integrating 1/l * fun(path(l)) with 0 <= l <= path length.

-

Parameters

-
-
fun : callable (3,) -> float
-
A function taking a three dimensional point and returning a float.
-
-

Returns

-
-
float
-
 
-
-

The average value of the function along the point.

-
-
-def close(self) -
-
-

Close the path, by making a straight line to the starting point.

-

Returns

-
-
Path
-
 
-
-
-
-def cut(self, length) -
-
-

Cut the path in two at a specific length along the path.

-

Parameters

-
-
length : float
-
The length along the path at which to cut.
-
-

Returns

-

(Path, Path)

-

A tuple containing two paths. The first path contains the path upto length, while the second path contains the rest.

-
-
-def endpoint(self) -
-
-

Returns the endpoint of the path.

-

Returns

-

(3,) float

-

The endpoint of the path.

-
-
-def extrude(self, vector) -
-
-

Create a surface by extruding the path along a vector. The vector gives both -the length and the direction of the extrusion.

-

Parameters

-
-
vector : (3,) float
-
The direction and length (norm of the vector) to extrude by.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def extrude_by_path(self, p2) -
-
-

Create a surface by extruding the path along a second path. The second -path does not need to start along the first path. Imagine the surface created -by moving the first path along the second path.

-

Parameters

-
-
p2 : Path
-
The (second) path defining the extrusion.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def is_closed(self) -
-
-

Determine whether the path is closed, by comparing the starting and endpoint.

-

Returns

-

bool: True if the path is closed, False otherwise.

-
-
-def line_to(self, point) -
-
-

Extend the current path by a line from the current endpoint to the given point. -The given point is marked a breakpoint.

-

Parameters

-
-
point : (3,) float
-
The new endpoint.
-
-

Returns

-
-
Path
-
 
-
-
-
-def map_points(self, fun) -
-
-

Return a new function by mapping a function over points along the path (see GeometricObject). -The path length is assumed to stay the same after this operation.

-

Parameters

-
-
fun : callable (3,) -> (3,)
-
Function taking three dimensional points and returning three dimensional points.
-
-

Returns

-
-
Path
-
 
-
-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False, name=None) -
-
-

Mesh the path, so it can be used in the BEM solver.

-

Parameters

-
-
mesh_size : float
-
Determines amount of elements in the mesh. A smaller -mesh size leads to more elements.
-
mesh_size_factor : float
-
Alternative way to specify the mesh size, which scales -with the dimensions of the geometry, and therefore more -easily translates between different geometries.
-
higher_order : bool
-
Whether to generate a higher order mesh. A higher order -produces curved line elements (determined by 4 points on -each curved element). The BEM solver supports higher order -elements in radial symmetric geometries only.
-
-

Returns

-
-
Path
-
 
-
-
-
-def middle_point(self) -
-
-

Returns the midpoint of the path (in terms of length along the path.)

-

Returns

-

(3,) float

-

The point at the middle of the path.

-
-
-def revolve_x(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the x-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def revolve_y(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the y-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def revolve_z(self, angle=6.283185307179586) -
-
-

Create a surface by revolving the path anti-clockwise around the z-axis.

-

Parameters

-
-
angle : float
-
The angle by which to revolve. THe default 2*pi gives a full revolution.
-
-

Returns

-
-
Surface
-
 
-
-
-
-def starting_point(self) -
-
-

Returns the starting point of the path.

-

Returns

-

(3,) float

-

The starting point of the path.

-
-
-

Inherited members

- -
-
-class PathCollection -(paths) -
-
-

A PathCollection is a collection of Path. It can be created using the + operator (for example path1+path2). -Note that PathCollection is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class PathCollection(GeometricObject):
-    """A PathCollection is a collection of `Path`. It can be created using the + operator (for example path1+path2).
-    Note that `PathCollection` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-    
-    def __init__(self, paths):
-        assert all([isinstance(p, Path) for p in paths])
-        self.paths = paths
-        self._name = None
-    
-    @property
-    def name(self):
-        return self._name
-
-    @name.setter
-    def name(self, name):
-        self._name = name
-         
-        for path in self.paths:
-            path.name = name
-     
-    def map_points(self, fun):
-        return PathCollection([p.map_points(fun) for p in self.paths])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False, name=None):
-        mesh = Mesh()
-        
-        name = self.name if name is None else name
-        
-        for p in self.paths:
-            mesh = mesh + p.mesh(mesh_size=mesh_size, mesh_size_factor=mesh_size_factor, higher_order=higher_order, name=name)
-
-        return mesh
-
-    def _map_to_surfaces(self, f, *args, **kwargs):
-        surfaces = []
-
-        for p in self.paths:
-            surfaces.append(f(p, *args, **kwargs))
-
-        return SurfaceCollection(surfaces)
-    
-    def __add__(self, other):
-        if not isinstance(other, Path) and not isinstance(other, PathCollection):
-            return NotImplemented
-        
-        if isinstance(other, Path):
-            return PathCollection(self.paths+[other])
-        else:
-            return PathCollection(self.paths+other.paths)
-      
-    def __iadd__(self, other):
-        assert isinstance(other, PathCollection) or isinstance(other, Path)
-
-        if isinstance(other, Path):
-            self.paths.append(other)
-        else:
-            self.paths.extend(other.paths)
-       
-    def revolve_x(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_x, angle=angle)
-    def revolve_y(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_y, angle=angle)
-    def revolve_z(self, angle=2*pi):
-        return self._map_to_surfaces(Path.revolve_z, angle=angle)
-    def extrude(self, vector):
-        return self._map_to_surfaces(Path.extrude, vector)
-    def extrude_by_path(self, p2):
-        return self._map_to_surfaces(Path.extrude_by_path, p2)
-    
-    def __str__(self):
-        return f"<PathCollection with {len(self.paths)} surfaces, name: {self.name}>"
-
-

Ancestors

- -

Instance variables

-
-
prop name
-
-
-
- -Expand source code - -
@property
-def name(self):
-    return self._name
-
-
-
-

Methods

-
-
-def extrude(self, vector) -
-
-
-
-
-def extrude_by_path(self, p2) -
-
-
-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, higher_order=False, name=None) -
-
-
-
-
-def revolve_x(self, angle=6.283185307179586) -
-
-
-
-
-def revolve_y(self, angle=6.283185307179586) -
-
-
-
-
-def revolve_z(self, angle=6.283185307179586) -
-
-
-
-
-

Inherited members

- -
-
-class Surface -(fun, path_length1, path_length2, breakpoints1=[], breakpoints2=[], name=None) -
-
-

A Surface is a mapping from two numbers to a three dimensional point. -Note that Surface is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Surface(GeometricObject):
-    """A Surface is a mapping from two numbers to a three dimensional point.
-    Note that `Surface` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-
-    def __init__(self, fun, path_length1, path_length2, breakpoints1=[], breakpoints2=[], name=None):
-        self.fun = fun
-        self.path_length1 = path_length1
-        self.path_length2 = path_length2
-        assert self.path_length1 > 0 and self.path_length2 > 0
-        self.breakpoints1 = breakpoints1
-        self.breakpoints2 = breakpoints2
-        self.name = name
-
-    def sections(self): 
-        # Iterate over the sections defined by
-        # the breakpoints
-        b1 = [0.] + self.breakpoints1 + [self.path_length1]
-        b2 = [0.] + self.breakpoints2 + [self.path_length2]
-
-        for u0, u1 in zip(b1[:-1], b1[1:]):
-            for v0, v1 in zip(b2[:-1], b2[1:]):
-                def fun(u, v, u0_=u0, v0_=v0):
-                    return self(u0_+u, v0_+v)
-                yield Surface(fun, u1-u0, v1-v0, [], [])
-       
-    def __call__(self, u, v):
-        return self.fun(u, v)
-
-    def map_points(self, fun):
-        return Surface(lambda u, v: fun(self(u, v)),
-            self.path_length1, self.path_length2,
-            self.breakpoints1, self.breakpoints2)
-     
-    @staticmethod
-    def spanned_by_paths(path1, path2):
-        length1 = max(path1.path_length, path2.path_length)
-        
-        length_start = np.linalg.norm(path1.starting_point() - path2.starting_point())
-        length_final = np.linalg.norm(path1.endpoint() - path2.endpoint())
-        length2 = (length_start + length_final)/2
-         
-        def f(u, v):
-            p1 = path1(u/length1*path1.path_length) # u/l*p = b, u = l*b/p
-            p2 = path2(u/length1*path2.path_length)
-            return (1-v/length2)*p1 + v/length2*p2
-
-        breakpoints = sorted([length1*b/path1.path_length for b in path1.breakpoints] + \
-                                [length1*b/path2.path_length for b in path2.breakpoints])
-         
-        return Surface(f, length1, length2, breakpoints)
-
-    @staticmethod
-    def sphere(radius):
-        
-        length1 = 2*pi*radius
-        length2 = pi*radius
-         
-        def f(u, v):
-            phi = u/radius
-            theta = v/radius
-            
-            return np.array([
-                radius*sin(theta)*cos(phi),
-                radius*sin(theta)*sin(phi),
-                radius*cos(theta)]) 
-        
-        return Surface(f, length1, length2)
-
-    @staticmethod
-    def box(p0, p1):
-        x0, y0, z0 = p0
-        x1, y1, z1 = p1
-
-        xmin, ymin, zmin = min(x0, x1), min(y0, y1), min(z0, z1)
-        xmax, ymax, zmax = max(x0, x1), max(y0, y1), max(z0, z1)
-        
-        path1 = Path.line([xmin, ymin, zmax], [xmax, ymin, zmax])
-        path2 = Path.line([xmin, ymin, zmin], [xmax, ymin, zmin])
-        path3 = Path.line([xmin, ymax, zmax], [xmax, ymax, zmax])
-        path4 = Path.line([xmin, ymax, zmin], [xmax, ymax, zmin])
-        
-        side_path = Path.line([xmin, ymin, zmin], [xmax, ymin, zmin])\
-            .line_to([xmax, ymin, zmax])\
-            .line_to([xmin, ymin, zmax])\
-            .close()
-
-        side_surface = side_path.extrude([0.0, ymax-ymin, 0.0])
-        top = Surface.spanned_by_paths(path1, path2)
-        bottom = Surface.spanned_by_paths(path4, path3)
-         
-        return (top + bottom + side_surface)
-
-    @staticmethod
-    def from_boundary_paths(p1, p2, p3, p4):
-        path_length_p1_and_p3 = (p1.path_length + p3.path_length)/2
-        path_length_p2_and_p4 = (p2.path_length + p4.path_length)/2
-
-        def f(u, v):
-            u /= path_length_p1_and_p3
-            v /= path_length_p2_and_p4
-            
-            a = (1-v)
-            b = (1-u)
-             
-            c = v
-            d = u
-            
-            return 1/2*(a*p1(u*p1.path_length) + \
-                        b*p4((1-v)*p4.path_length) + \
-                        c*p3((1-u)*p3.path_length) + \
-                        d*p2(v*p2.path_length))
-        
-        # Scale the breakpoints appropriately
-        b1 = sorted([b/p1.path_length * path_length_p1_and_p3 for b in p1.breakpoints] + \
-                [b/p3.path_length * path_length_p1_and_p3 for b in p3.breakpoints])
-        b2 = sorted([b/p2.path_length * path_length_p2_and_p4 for b in p2.breakpoints] + \
-                [b/p4.path_length * path_length_p2_and_p4 for b in p4.breakpoints])
-        
-        return Surface(f, path_length_p1_and_p3, path_length_p2_and_p4, b1, b2)
-     
-    @staticmethod
-    def disk_xz(x0, z0, radius):
-        """Create a disk in the XZ plane.         
-        
-        Parameters
-        ------------------------
-        x0: float
-            x-coordiante of the center of the disk
-        z0: float
-            z-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [radius, 0.0, 0.0]).revolve_y()
-        return disk_at_origin.move(dx=x0, dz=z0)
-    
-    @staticmethod
-    def disk_yz(y0, z0, radius):
-        """Create a disk in the YZ plane.         
-        
-        Parameters
-        ------------------------
-        y0: float
-            y-coordiante of the center of the disk
-        z0: float
-            z-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [0.0, radius, 0.0]).revolve_x()
-        return disk_at_origin.move(dy=y0, dz=z0)
-
-    @staticmethod
-    def disk_xy(x0, y0, radius):
-        """Create a disk in the XY plane.
-        
-        Parameters
-        ------------------------
-        x0: float
-            x-coordiante of the center of the disk
-        y0: float
-            y-coordinate of the center of the disk
-        radius: float
-            radius of the disk
-        Returns
-        -----------------------
-        Surface"""
-        assert radius > 0, "radius must be a positive number"
-        disk_at_origin = Path.line([0.0, 0.0, 0.0], [radius, 0.0, 0.0]).revolve_z()
-        return disk_at_origin.move(dx=x0, dy=y0)
-     
-    @staticmethod
-    def rectangle_xz(xmin, xmax, zmin, zmax):
-        """Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is 
-        counter clockwise around the y-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([xmin, 0., zmin], [xmin, 0, zmax]).extrude([xmax-xmin, 0., 0.])
-     
-    @staticmethod
-    def rectangle_yz(ymin, ymax, zmin, zmax):
-        """Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is 
-        counter clockwise around the x-axis.
-        
-        Parameters
-        ------------------------
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        zmin: float
-            Minimum z-coordinate of the corner points.
-        zmax: float
-            Maximum z-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([0., ymin, zmin], [0., ymin, zmax]).extrude([0., ymax-ymin, 0.])
-     
-    @staticmethod
-    def rectangle_xy(xmin, xmax, ymin, ymax):
-        """Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is 
-        counter clockwise around the z-axis.
-        
-        Parameters
-        ------------------------
-        xmin: float
-            Minimum x-coordinate of the corner points.
-        xmax: float
-            Maximum x-coordinate of the corner points.
-        ymin: float
-            Minimum y-coordinate of the corner points.
-        ymax: float
-            Maximum y-coordinate of the corner points.
-        
-        Returns
-        -----------------------
-        Surface representing the rectangle"""
-        return Path.line([xmin, ymin, 0.], [xmin, ymax, 0.]).extrude([xmax-xmin, 0., 0.])
-
-    @staticmethod
-    def aperture(height, radius, extent, z=0.):
-        return Path.aperture(height, radius, extent, z=z).revolve_z()
-     
-    def __add__(self, other):
-        if not isinstance(other, Surface) and not isinstance(other, SurfaceCollection):
-            return NotImplemented
-
-        if isinstance(other, Surface):
-            return SurfaceCollection([self, other])
-        else:
-            return SurfaceCollection([self] + other.surfaces)
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, name=None):
-          
-        if mesh_size is None:
-            path_length = min(self.path_length1, self.path_length2)
-             
-            mesh_size = path_length / 4
-
-            if mesh_size_factor is not None:
-                mesh_size /= sqrt(mesh_size_factor)
-
-        name = self.name if name is None else name
-        return _mesh(self, mesh_size, name=name)
-    
-    def __str__(self):
-        return f"<Surface with name: {self.name}>"
-
-

Ancestors

- -

Static methods

-
-
-def aperture(height, radius, extent, z=0.0) -
-
-
-
-
-def box(p0, p1) -
-
-
-
-
-def disk_xy(x0, y0, radius) -
-
-

Create a disk in the XY plane.

-

Parameters

-
-
x0 : float
-
x-coordiante of the center of the disk
-
y0 : float
-
y-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def disk_xz(x0, z0, radius) -
-
-

Create a disk in the XZ plane. -

-

Parameters

-
-
x0 : float
-
x-coordiante of the center of the disk
-
z0 : float
-
z-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def disk_yz(y0, z0, radius) -
-
-

Create a disk in the YZ plane. -

-

Parameters

-
-
y0 : float
-
y-coordiante of the center of the disk
-
z0 : float
-
z-coordinate of the center of the disk
-
radius : float
-
radius of the disk
-
-

Returns

-
-
Surface
-
 
-
-
-
-def from_boundary_paths(p1, p2, p3, p4) -
-
-
-
-
-def rectangle_xy(xmin, xmax, ymin, ymax) -
-
-

Create a rectangle in the XY plane. The path starts at (xmin, ymin, 0), and is -counter clockwise around the z-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def rectangle_xz(xmin, xmax, zmin, zmax) -
-
-

Create a rectangle in the XZ plane. The path starts at (xmin, 0, zmin), and is -counter clockwise around the y-axis.

-

Parameters

-
-
xmin : float
-
Minimum x-coordinate of the corner points.
-
xmax : float
-
Maximum x-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def rectangle_yz(ymin, ymax, zmin, zmax) -
-
-

Create a rectangle in the YZ plane. The path starts at (0, ymin, zmin), and is -counter clockwise around the x-axis.

-

Parameters

-
-
ymin : float
-
Minimum y-coordinate of the corner points.
-
ymax : float
-
Maximum y-coordinate of the corner points.
-
zmin : float
-
Minimum z-coordinate of the corner points.
-
zmax : float
-
Maximum z-coordinate of the corner points.
-
-

Returns

-
-
Surface representing the rectangle
-
 
-
-
-
-def spanned_by_paths(path1, path2) -
-
-
-
-
-def sphere(radius) -
-
-
-
-
-

Methods

-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, name=None) -
-
-
-
-
-def sections(self) -
-
-
-
-
-

Inherited members

- -
-
-class SurfaceCollection -(surfaces) -
-
-

A SurfaceCollection is a collection of Surface. It can be created using the + operator (for example surface1+surface2). -Note that SurfaceCollection is a subclass of GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class SurfaceCollection(GeometricObject):
-    """A SurfaceCollection is a collection of `Surface`. It can be created using the + operator (for example surface1+surface2).
-    Note that `SurfaceCollection` is a subclass of `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-     
-    def __init__(self, surfaces):
-        assert all([isinstance(s, Surface) for s in surfaces])
-        self.surfaces = surfaces
-        self._name = None
-
-    @property
-    def name(self):
-        return self._name
-
-    @name.setter
-    def name(self, name):
-        self._name = name
-         
-        for surf in self.surfaces:
-            surf.name = name
-     
-    def map_points(self, fun):
-        return SurfaceCollection([s.map_points(fun) for s in self.surfaces])
-     
-    def mesh(self, mesh_size=None, mesh_size_factor=None, name=None):
-        mesh = Mesh()
-        
-        name = self.name if name is None else name
-        
-        for s in self.surfaces:
-            mesh = mesh + s.mesh(mesh_size=mesh_size, mesh_size_factor=mesh_size_factor, name=name)
-         
-        return mesh
-     
-    def __add__(self, other):
-        if not isinstance(other, Surface) and not isinstance(other, SurfaceCollection):
-            return NotImplemented
-              
-        if isinstance(other, Surface):
-            return SurfaceCollection(self.surfaces+[other])
-        else:
-            return SurfaceCollection(self.surfaces+other.surfaces)
-     
-    def __iadd__(self, other):
-        assert isinstance(other, SurfaceCollection) or isinstance(other, Surface)
-        
-        if isinstance(other, Surface):
-            self.surfaces.append(other)
-        else:
-            self.surfaces.extend(other.surfaces)
-
-    def __str__(self):
-        return f"<SurfaceCollection with {len(self.surfaces)} surfaces, name: {self.name}>"
-
-

Ancestors

- -

Instance variables

-
-
prop name
-
-
-
- -Expand source code - -
@property
-def name(self):
-    return self._name
-
-
-
-

Methods

-
-
-def mesh(self, mesh_size=None, mesh_size_factor=None, name=None) -
-
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc2/index.html b/docs/docs/v0.7.0rc2/index.html deleted file mode 100644 index 60dab098..00000000 --- a/docs/docs/v0.7.0rc2/index.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - -traceon API documentation - - - - - - - - - - - - - -
-
-
-

Package traceon

-
-
-

Welcome!

-

Traceon is a general software package used for numerical electron optics. Its main feature is the implementation of the Boundary Element Method (BEM) to quickly calculate the surface charge distribution. -The program supports both radial symmetry and general three-dimensional geometries. -Electron tracing can be done very quickly using accurate radial series interpolation in both geometries. -The electron trajectories obtained can help determine the aberrations of the optical components under study.

-

If you have any issues using the package, please open an issue on the Traceon Github page.

-

The software is currently distributed under the AGPLv3 license.

-

Usage

-

In general, one starts with the traceon.geometry module to create a mesh. For the BEM only the boundary of -electrodes needs to be meshed. So in 2D (radial symmetry) the mesh consists of line elements while in 3D the -mesh consists of triangles. -Next, one specifies a suitable excitation (voltages) using the traceon.excitation module. -The excited geometry can then be passed to the solve_direct() function, which computes the resulting field. -The field can be passed to the Tracer class to compute the trajectory of electrons moving through the field.

-

Validations

-

To make sure the software is correct, various problems from the literature with known solutions are analyzed using the Traceon software and the -results compared. In this manner it has been shown that the software produces very accurate results very quickly. The validations can be found in the -/validations directory in the Github project. After installing Traceon, the validations can be -executed as follows:

-
    git clone https://github.com/leon-vv/Traceon
-    cd traceon
-    python3 ./validation/edwards2007.py --help
-
-

Units

-

SI units are used throughout the codebase. Except for charge, which is stored as \frac{ \sigma}{ \epsilon_0} .

-
-
-

Sub-modules

-
-
traceon.excitation
-
-

The excitation module allows to specify the excitation (or element types) of the different physical groups (electrodes) -created with the …

-
-
traceon.focus
-
-

Module containing a single function to find the focus of a beam of electron trajecories.

-
-
traceon.geometry
-
-

The geometry module allows the creation of general meshes in 2D and 3D. -The builtin mesher uses so called parametric meshes, meaning -that for any …

-
-
traceon.interpolation
-
-
-
-
traceon.logging
-
-
-
-
traceon.mesher
-
-
-
-
traceon.plotting
-
-

The traceon.plotting module uses the vedo plotting library to provide some convenience functions -to show the line and triangle meshes generated by …

-
-
traceon.solver
-
-

The solver module uses the Boundary Element Method (BEM) to compute the surface charge distribution of a given -geometry and excitation. Once the …

-
-
traceon.tracing
-
-

The tracing module allows to trace electrons within any field type returned by the traceon.solver module. The tracing algorithm -used is RK45 with …

-
-
-
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc2/logging.html b/docs/docs/v0.7.0rc2/logging.html deleted file mode 100644 index 7142c272..00000000 --- a/docs/docs/v0.7.0rc2/logging.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - -traceon.logging API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.logging

-
-
-
-
-
-
-
-
-

Functions

-
-
-def set_log_level(level) -
-
-

Set the current LogLevel. Note that the log level can also -be set by setting the environment value TRACEON_LOG_LEVEL to one -of 'debug', 'info', 'warning', 'error' or 'silent'.

-
-
-
-
-

Classes

-
-
-class LogLevel -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Enumeration representing a certain verbosity of logging.

-
- -Expand source code - -
class LogLevel(IntEnum):
-    """Enumeration representing a certain verbosity of logging."""
-    
-    DEBUG = 0
-    """Print debug, info, warning and error information."""
-
-    INFO = 1
-    """Print info, warning and error information."""
-
-    WARNING = 2
-    """Print only warnings and errors."""
-
-    ERROR = 3
-    """Print only errors."""
-     
-    SILENT = 4
-    """Do not print anything."""
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var DEBUG
-
-

Print debug, info, warning and error information.

-
-
var ERROR
-
-

Print only errors.

-
-
var INFO
-
-

Print info, warning and error information.

-
-
var SILENT
-
-

Do not print anything.

-
-
var WARNING
-
-

Print only warnings and errors.

-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc2/mesher.html b/docs/docs/v0.7.0rc2/mesher.html deleted file mode 100644 index 62cd28c0..00000000 --- a/docs/docs/v0.7.0rc2/mesher.html +++ /dev/null @@ -1,919 +0,0 @@ - - - - - - -traceon.mesher API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.mesher

-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class GeometricObject -
-
-

The Mesh class (and the classes defined in traceon.geometry) are subclasses -of GeometricObject. This means that they all can be moved, rotated, mirrored.

-
- -Expand source code - -
class GeometricObject(ABC):
-    """The Mesh class (and the classes defined in `traceon.geometry`) are subclasses
-    of GeometricObject. This means that they all can be moved, rotated, mirrored."""
-    
-    @abstractmethod
-    def map_points(self, fun: Callable[[np.ndarray], np.ndarray]) -> Any:
-        """Create a new geometric object, by mapping each point by a function.
-        
-        Parameters
-        -------------------------
-        fun: (3,) float -> (3,) float
-            Function taking a three dimensional point and returning a 
-            three dimensional point.
-
-        Returns
-        ------------------------
-        GeometricObject
-
-        This function returns the same type as the object on which this method was called."""
-        ...
-    
-    def move(self, dx=0., dy=0., dz=0.):
-        """Move along x, y or z axis.
-
-        Parameters
-        ---------------------------
-        dx: float
-            Amount to move along the x-axis.
-        dy: float
-            Amount to move along the y-axis.
-        dz: float
-            Amount to move along the z-axis.
-
-        Returns
-        ---------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-    
-        assert all([isinstance(d, float) or isinstance(d, int) for d in [dx, dy, dz]])
-        return self.map_points(lambda p: p + np.array([dx, dy, dz]))
-     
-    def rotate(self, Rx=0., Ry=0., Rz=0., origin=[0., 0., 0.]):
-        """Rotate counter clockwise around the x, y or z axis. Only one axis supported at the same time
-        (rotations do not commute).
-
-        Parameters
-        ------------------------------------
-        Rx: float
-            Amount to rotate around the x-axis (radians).
-        Ry: float
-            Amount to rotate around the y-axis (radians).
-        Rz: float
-            Amount to rotate around the z-axis (radians).
-        origin: (3,) float
-            Point around which to rotate, which is the origin by default.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        
-        assert sum([Rx==0., Ry==0., Rz==0.]) >= 2, "Only supply one axis of rotation"
-        origin = np.array(origin)
-        assert origin.shape == (3,), "Please supply a 3D point for origin"
-         
-        if Rx != 0.:
-            matrix = np.array([[1, 0, 0],
-                [0, np.cos(Rx), -np.sin(Rx)],
-                [0, np.sin(Rx), np.cos(Rx)]])
-        elif Ry != 0.:
-            matrix = np.array([[np.cos(Ry), 0, np.sin(Ry)],
-                [0, 1, 0],
-                [-np.sin(Ry), 0, np.cos(Ry)]])
-        elif Rz != 0.:
-            matrix = np.array([[np.cos(Rz), -np.sin(Rz), 0],
-                [np.sin(Rz), np.cos(Rz), 0],
-                [0, 0, 1]])
-
-        return self.map_points(lambda p: origin + matrix @ (p - origin))
-
-    def mirror_xz(self):
-        """Mirror object in the XZ plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([p[0], -p[1], p[2]]))
-     
-    def mirror_yz(self):
-        """Mirror object in the YZ plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([-p[0], p[1], p[2]]))
-    
-    def mirror_xy(self):
-        """Mirror object in the XY plane.
-
-        Returns
-        --------------------------------------
-        GeometricObject
-        
-        This function returns the same type as the object on which this method was called."""
-        return self.map_points(lambda p: np.array([p[0], p[1], -p[2]]))
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def map_points(self, fun: Callable[[numpy.ndarray], numpy.ndarray]) ‑> Any -
-
-

Create a new geometric object, by mapping each point by a function.

-

Parameters

-
-
fun : (3,) float -> (3,) float
-
Function taking a three dimensional point and returning a -three dimensional point.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_xy(self) -
-
-

Mirror object in the XY plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_xz(self) -
-
-

Mirror object in the XZ plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def mirror_yz(self) -
-
-

Mirror object in the YZ plane.

-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def move(self, dx=0.0, dy=0.0, dz=0.0) -
-
-

Move along x, y or z axis.

-

Parameters

-
-
dx : float
-
Amount to move along the x-axis.
-
dy : float
-
Amount to move along the y-axis.
-
dz : float
-
Amount to move along the z-axis.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-def rotate(self, Rx=0.0, Ry=0.0, Rz=0.0, origin=[0.0, 0.0, 0.0]) -
-
-

Rotate counter clockwise around the x, y or z axis. Only one axis supported at the same time -(rotations do not commute).

-

Parameters

-
-
Rx : float
-
Amount to rotate around the x-axis (radians).
-
Ry : float
-
Amount to rotate around the y-axis (radians).
-
Rz : float
-
Amount to rotate around the z-axis (radians).
-
origin : (3,) float
-
Point around which to rotate, which is the origin by default.
-
-

Returns

-
-
GeometricObject
-
 
-
-

This function returns the same type as the object on which this method was called.

-
-
-
-
-class Mesh -(points=[], lines=[], triangles=[], physical_to_lines={}, physical_to_triangles={}) -
-
-

Mesh containing lines and triangles. Groups of lines or triangles can be named. These -names are later used to apply the correct excitation. Line elements can be curved (or 'higher order'), -in which case they are represented by four points per element. -Note that Mesh is a subclass of -GeometricObject, and therefore can be easily moved and rotated.

-
- -Expand source code - -
class Mesh(Saveable, GeometricObject):
-    """Mesh containing lines and triangles. Groups of lines or triangles can be named. These
-    names are later used to apply the correct excitation. Line elements can be curved (or 'higher order'), 
-    in which case they are represented by four points per element.  Note that `Mesh` is a subclass of
-    `traceon.mesher.GeometricObject`, and therefore can be easily moved and rotated."""
-     
-    def __init__(self,
-            points=[],
-            lines=[],
-            triangles=[],
-            physical_to_lines={},
-            physical_to_triangles={}):
-        
-        # Ensure the correct shape even if empty arrays
-        if len(points):
-            self.points = np.array(points, dtype=np.float64)
-        else:
-            self.points = np.empty((0,3), dtype=np.float64)
-         
-        if len(lines) or (isinstance(lines, np.ndarray) and len(lines.shape)==2):
-            self.lines = np.array(lines, dtype=np.uint64)
-        else:
-            self.lines = np.empty((0,2), dtype=np.uint64)
-    
-        if len(triangles):
-            self.triangles = np.array(triangles, dtype=np.uint64)
-        else:
-            self.triangles = np.empty((0, 3), dtype=np.uint64)
-         
-        self.physical_to_lines = physical_to_lines.copy()
-        self.physical_to_triangles = physical_to_triangles.copy()
-
-        self._remove_degenerate_triangles()
-        
-        assert np.all( (0 <= self.lines) & (self.lines < len(self.points)) ), "Lines reference points outside points array"
-        assert np.all( (0 <= self.triangles) & (self.triangles < len(self.points)) ), "Triangles reference points outside points array"
-        assert np.all([np.all( (0 <= group) & (group < len(self.lines)) ) for group in self.physical_to_lines.values()])
-        assert np.all([np.all( (0 <= group) & (group < len(self.triangles)) ) for group in self.physical_to_triangles.values()])
-        assert not len(self.lines) or self.lines.shape[1] in [2,4], "Lines should contain either 2 or 4 points."
-        assert not len(self.triangles) or self.triangles.shape[1] in [3,6], "Triangles should contain either 3 or 6 points"
-    
-    def is_higher_order(self):
-        """Whether the mesh contains higher order elements.
-
-        Returns
-        ----------------------------
-        bool"""
-        return isinstance(self.lines, np.ndarray) and len(self.lines.shape) == 2 and self.lines.shape[1] == 4
-    
-    def map_points(self, fun):
-        """See `GeometricObject`
-
-        """
-        new_points = np.empty_like(self.points)
-        for i in range(len(self.points)):
-            new_points[i] = fun(self.points[i])
-        assert new_points.shape == self.points.shape and new_points.dtype == self.points.dtype
-        
-        return Mesh(new_points, self.lines, self.triangles, self.physical_to_lines, self.physical_to_triangles)
-    
-    def _remove_degenerate_triangles(self):
-        areas = triangle_areas(self.points[self.triangles[:,:3]])
-        degenerate = areas < 1e-12
-        map_index = np.arange(len(self.triangles)) - np.cumsum(degenerate)
-         
-        self.triangles = self.triangles[~degenerate]
-        
-        for k in self.physical_to_triangles.keys():
-            v = self.physical_to_triangles[k]
-            self.physical_to_triangles[k] = map_index[v[~degenerate[v]]]
-         
-        if np.any(degenerate):
-            log_debug(f'Removed {sum(degenerate)} degenerate triangles')
-    
-    @staticmethod
-    def _merge_dicts(dict1, dict2):
-        dict_: dict[str, np.ndarray] = {}
-        
-        for (k, v) in chain(dict1.items(), dict2.items()):
-            if k in dict_:
-                dict_[k] = np.concatenate( (dict_[k], v), axis=0)
-            else:
-                dict_[k] = v
-
-        return dict_
-     
-    def __add__(self, other):
-        """Add meshes together, using the + operator (mesh1 + mesh2).
-        
-        Returns
-        ------------------------------
-        Mesh
-
-        A new mesh consisting of the elements of the added meshes"""
-        if not isinstance(other, Mesh):
-            return NotImplemented
-         
-        N_points = len(self.points)
-        N_lines = len(self.lines)
-        N_triangles = len(self.triangles)
-         
-        points = _concat_arrays(self.points, other.points)
-        lines = _concat_arrays(self.lines, other.lines+N_points)
-        triangles = _concat_arrays(self.triangles, other.triangles+N_points)
-
-        other_physical_to_lines = {k:(v+N_lines) for k, v in other.physical_to_lines.items()}
-        other_physical_to_triangles = {k:(v+N_triangles) for k, v in other.physical_to_triangles.items()}
-         
-        physical_lines = Mesh._merge_dicts(self.physical_to_lines, other_physical_to_lines)
-        physical_triangles = Mesh._merge_dicts(self.physical_to_triangles, other_physical_to_triangles)
-         
-        return Mesh(points=points,
-                    lines=lines,
-                    triangles=triangles,
-                    physical_to_lines=physical_lines,
-                    physical_to_triangles=physical_triangles)
-     
-    def extract_physical_group(self, name):
-        """Extract a named group from the mesh.
-
-        Parameters
-        ---------------------------
-        name: str
-            Name of the group of elements
-
-        Returns
-        --------------------------
-        Mesh
-
-        Subset of the mesh consisting only of the elements with the given name."""
-        assert name in self.physical_to_lines or name in self.physical_to_triangles, "Physical group not in mesh, so cannot extract"
-
-        if name in self.physical_to_lines:
-            elements = self.lines
-            physical = self.physical_to_lines
-        elif name in self.physical_to_triangles:
-            elements = self.triangles
-            physical = self.physical_to_triangles
-         
-        elements_indices = np.unique(physical[name])
-        elements = elements[elements_indices]
-          
-        points_mask = np.full(len(self.points), False)
-        points_mask[elements] = True
-        points = self.points[points_mask]
-          
-        new_index = np.cumsum(points_mask) - 1
-        elements = new_index[elements]
-        physical_to_elements = {name:np.arange(len(elements))}
-         
-        if name in self.physical_to_lines:
-            return Mesh(points=points, lines=elements, physical_to_lines=physical_to_elements)
-        elif name in self.physical_to_triangles:
-            return Mesh(points=points, triangles=elements, physical_to_triangles=physical_to_elements)
-     
-    @staticmethod
-    def read_file(filename,  name=None):
-        """Create a mesh from a given file. All formats supported by meshio are accepted.
-
-        Parameters
-        ------------------------------
-        filename: str
-            Path of the file to convert to Mesh
-        name: str
-            (optional) name to assign to all elements readed
-
-        Returns
-        -------------------------------
-        Mesh"""
-        meshio_obj = meshio.read(filename)
-        mesh = Mesh.from_meshio(meshio_obj)
-         
-        if name is not None:
-            mesh.physical_to_lines[name] = np.arange(len(mesh.lines))
-            mesh.physical_to_triangles[name] = np.arange(len(mesh.triangles))
-         
-        return mesh
-     
-    def write_file(self, filename):
-        """Write a mesh to a given file. The format is determined from the file extension.
-        All formats supported by meshio are supported.
-
-        Parameters
-        ----------------------------------
-        filename: str
-            The name of the file to write the mesh to."""
-        meshio_obj = self.to_meshio()
-        meshio_obj.write(filename)
-    
-    def write(self, filename):
-        self.write_file(filename)
-        
-     
-    def to_meshio(self):
-        """Convert the Mesh to a meshio object.
-
-        Returns
-        ------------------------------------
-        meshio.Mesh"""
-        to_write = []
-        
-        if len(self.lines):
-            line_type = 'line' if self.lines.shape[1] == 2 else 'line4'
-            to_write.append( (line_type, self.lines) )
-        
-        if len(self.triangles):
-            triangle_type = 'triangle' if self.triangles.shape[1] == 3 else 'triangle6'
-            to_write.append( (triangle_type, self.triangles) )
-        
-        return meshio.Mesh(self.points, to_write)
-     
-    @staticmethod
-    def from_meshio(mesh: meshio.Mesh):
-        """Create a Traceon mesh from a meshio.Mesh object.
-
-        Parameters
-        --------------------------
-        mesh: meshio.Mesh
-            The mesh to convert to a Traceon mesh
-
-        Returns
-        -------------------------
-        Mesh"""
-        def extract(type_):
-            elements = mesh.cells_dict[type_]
-            physical = {k:v[type_] for k,v in mesh.cell_sets_dict.items() if type_ in v}
-            return elements, physical
-        
-        lines, physical_lines = [], {}
-        triangles, physical_triangles = [], {}
-        
-        if 'line' in mesh.cells_dict:
-            lines, physical_lines = extract('line')
-        elif 'line4' in mesh.cells_dict:
-            lines, physical_lines = extract('line4')
-        
-        if 'triangle' in mesh.cells_dict:
-            triangles, physical_triangles = extract('triangle')
-        elif 'triangle6' in mesh.cells_dict:
-            triangles, physical_triangles = extract('triangle6')
-        
-        return Mesh(points=mesh.points,
-            lines=lines, physical_to_lines=physical_lines,
-            triangles=triangles, physical_to_triangles=physical_triangles)
-     
-    def is_3d(self):
-        """Check if the mesh is three dimensional by checking whether any z coordinate is non-zero.
-
-        Returns
-        ----------------
-        bool
-
-        Whether the mesh is three dimensional"""
-        return np.any(self.points[:, 1] != 0.)
-    
-    def is_2d(self):
-        """Check if the mesh is two dimensional, by checking that all z coordinates are zero.
-        
-        Returns
-        ----------------
-        bool
-
-        Whether the mesh is two dimensional"""
-        return np.all(self.points[:, 1] == 0.)
-    
-    def flip_normals(self):
-        """Flip the normals in the mesh by inverting the 'orientation' of the elements.
-
-        Returns
-        ----------------------------
-        Mesh"""
-        lines = self.lines
-        triangles = self.triangles
-        
-        # Flip the orientation of the lines
-        if lines.shape[1] == 4:
-            p0, p1, p2, p3 = lines.T
-            lines = np.array([p1, p0, p3, p2]).T
-        else:
-            p0, p1 = lines.T
-            lines = np.array([p1, p0]).T
-          
-        # Flip the orientation of the triangles
-        if triangles.shape[1] == 6:
-            p0, p1, p2, p3, p4, p5 = triangles.T
-            triangles = np.array([p0, p2, p1, p5, p4, p3]).T
-        else:
-            p0, p1, p2 = triangles.T
-            triangles = np.array([p0, p2, p1]).T
-        
-        return Mesh(self.points, lines, triangles,
-            physical_to_lines=self.physical_to_lines,
-            physical_to_triangles=self.physical_to_triangles)
-     
-    def remove_lines(self):
-        """Remove all the lines from the mesh.
-
-        Returns
-        -----------------------------
-        Mesh"""
-        return Mesh(self.points, triangles=self.triangles, physical_to_triangles=self.physical_to_triangles)
-    
-    def remove_triangles(self):
-        """Remove all triangles from the mesh.
-
-        Returns
-        -------------------------------------
-        Mesh"""
-        return Mesh(self.points, lines=self.lines, physical_to_lines=self.physical_to_lines)
-     
-    def get_electrodes(self):
-        """Get the names of all the named groups (i.e. electrodes) in the mesh
-         
-        Returns
-        ---------
-        str iterable
-
-        Names
-        """
-        return list(self.physical_to_lines.keys()) + list(self.physical_to_triangles.keys())
-     
-    @staticmethod
-    def _lines_to_higher_order(points, elements):
-        N_elements = len(elements)
-        N_points = len(points)
-         
-        v0, v1 = elements.T
-        p2 = points[v0] + (points[v1] - points[v0]) * 1/3
-        p3 = points[v0] + (points[v1] - points[v0]) * 2/3
-         
-        assert all(p.shape == (N_elements, points.shape[1]) for p in [p2, p3])
-         
-        points = np.concatenate( (points, p2, p3), axis=0)
-          
-        elements = np.array([
-            elements[:, 0], elements[:, 1], 
-            np.arange(N_points, N_points + N_elements, dtype=np.uint64),
-            np.arange(N_points + N_elements, N_points + 2*N_elements, dtype=np.uint64)]).T
-         
-        assert np.allclose(p2, points[elements[:, 2]]) and np.allclose(p3, points[elements[:, 3]])
-        return points, elements
-
-
-    def _to_higher_order_mesh(self):
-        # The matrix solver currently only works with higher order meshes.
-        # We can however convert a simple mesh easily to a higher order mesh, and solve that.
-        
-        points, lines, triangles = self.points, self.lines, self.triangles
-
-        if not len(lines):
-            lines = np.empty( (0, 4), dtype=np.float64)
-        elif len(lines) and lines.shape[1] == 2:
-            points, lines = Mesh._lines_to_higher_order(points, lines)
-        
-        assert lines.shape == (len(lines), 4)
-
-        return Mesh(points=points,
-            lines=lines, physical_to_lines=self.physical_to_lines,
-            triangles=triangles, physical_to_triangles=self.physical_to_triangles)
-     
-    def __str__(self):
-        physical_lines = ', '.join(self.physical_to_lines.keys())
-        physical_lines_nums = ', '.join([str(len(self.physical_to_lines[n])) for n in self.physical_to_lines.keys()])
-        physical_triangles = ', '.join(self.physical_to_triangles.keys())
-        physical_triangles_nums = ', '.join([str(len(self.physical_to_triangles[n])) for n in self.physical_to_triangles.keys()])
-        
-        return f'<Traceon Mesh,\n' \
-            f'\tNumber of points: {len(self.points)}\n' \
-            f'\tNumber of lines: {len(self.lines)}\n' \
-            f'\tNumber of triangles: {len(self.triangles)}\n' \
-            f'\tPhysical lines: {physical_lines}\n' \
-            f'\tElements in physical line groups: {physical_lines_nums}\n' \
-            f'\tPhysical triangles: {physical_triangles}\n' \
-            f'\tElements in physical triangle groups: {physical_triangles_nums}>'
-
-

Ancestors

- -

Static methods

-
-
-def from_meshio(mesh: meshio._mesh.Mesh) -
-
-

Create a Traceon mesh from a meshio.Mesh object.

-

Parameters

-
-
mesh : meshio.Mesh
-
The mesh to convert to a Traceon mesh
-
-

Returns

-
-
Mesh
-
 
-
-
-
-def read_file(filename, name=None) -
-
-

Create a mesh from a given file. All formats supported by meshio are accepted.

-

Parameters

-
-
filename : str
-
Path of the file to convert to Mesh
-
name : str
-
(optional) name to assign to all elements readed
-
-

Returns

-
-
Mesh
-
 
-
-
-
-

Methods

-
-
-def extract_physical_group(self, name) -
-
-

Extract a named group from the mesh.

-

Parameters

-
-
name : str
-
Name of the group of elements
-
-

Returns

-
-
Mesh
-
 
-
-

Subset of the mesh consisting only of the elements with the given name.

-
-
-def flip_normals(self) -
-
-

Flip the normals in the mesh by inverting the 'orientation' of the elements.

-

Returns

-
-
Mesh
-
 
-
-
-
-def get_electrodes(self) -
-
-

Get the names of all the named groups (i.e. electrodes) in the mesh

-

Returns

-
-
str iterable
-
 
-
Names
-
 
-
-
-
-def is_2d(self) -
-
-

Check if the mesh is two dimensional, by checking that all z coordinates are zero.

-

Returns

-
-
bool
-
 
-
Whether the mesh is two dimensional
-
 
-
-
-
-def is_3d(self) -
-
-

Check if the mesh is three dimensional by checking whether any z coordinate is non-zero.

-

Returns

-
-
bool
-
 
-
Whether the mesh is three dimensional
-
 
-
-
-
-def is_higher_order(self) -
-
-

Whether the mesh contains higher order elements.

-

Returns

-
-
bool
-
 
-
-
-
-def map_points(self, fun) -
-
- -
-
-def remove_lines(self) -
-
-

Remove all the lines from the mesh.

-

Returns

-
-
Mesh
-
 
-
-
-
-def remove_triangles(self) -
-
-

Remove all triangles from the mesh.

-

Returns

-
-
Mesh
-
 
-
-
-
-def to_meshio(self) -
-
-

Convert the Mesh to a meshio object.

-

Returns

-
-
meshio.Mesh
-
 
-
-
-
-def write(self, filename) -
-
-

Write a mesh to a file. The pickle module will be used -to save the Geometry object.

-

Args

-
-
filename
-
name of the file
-
-
-
-def write_file(self, filename) -
-
-

Write a mesh to a given file. The format is determined from the file extension. -All formats supported by meshio are supported.

-

Parameters

-
-
filename : str
-
The name of the file to write the mesh to.
-
-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc2/plotting.html b/docs/docs/v0.7.0rc2/plotting.html deleted file mode 100644 index a38e6723..00000000 --- a/docs/docs/v0.7.0rc2/plotting.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -traceon.plotting API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.plotting

-
-
-

The traceon.plotting module uses the vedo plotting library to provide some convenience functions -to show the line and triangle meshes generated by Traceon.

-
-
-
-
-
-
-

Functions

-
-
-def plot_mesh(mesh, show_normals=False, show_legend=True, **colors) -
-
-

Plot mesh using the Vedo library. Optionally showing normal vectors.

-

Parameters

-
-
show_normals : bool
-
Whether to show the normal vectors at every element
-
show_legend : bool
-
Whether to show the legend
-
colors : dict of (string, string)
-
Use keyword arguments to specify colors, for example plot_mesh(mesh, lens='blue', ground='green')
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/excitation.html b/docs/docs/v0.7.0rc3/excitation.html deleted file mode 100644 index 775fe97a..00000000 --- a/docs/docs/v0.7.0rc3/excitation.html +++ /dev/null @@ -1,662 +0,0 @@ - - - - - - -traceon.excitation API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.excitation

-
-
-

The excitation module allows to specify the excitation (or element types) of the different physical groups (electrodes) -created with the traceon.geometry module.

-

The possible excitations are as follows:

-
    -
  • Fixed voltage (electrode connect to a power supply)
  • -
  • Voltage function (a generic Python function specifies the voltage as a function of position)
  • -
  • Dielectric, with arbitrary electric permittivity
  • -
  • Current coil, with fixed total amount of current (only in radial symmetry)
  • -
  • Magnetostatic scalar potential
  • -
  • Magnetizable material, with arbitrary magnetic permeability
  • -
-

Currently current excitations are not supported in 3D. But magnetostatic fields can still be computed using the magnetostatic scalar potential.

-

Once the excitation is specified, it can be passed to solve_direct() to compute the resulting field.

-
-
-
-
-
-
-
-
-

Classes

-
-
-class Excitation -(mesh, symmetry) -
-
-
-
- -Expand source code - -
class Excitation:
-    """ """
-     
-    def __init__(self, mesh, symmetry):
-        self.mesh = mesh
-        self.electrodes = mesh.get_electrodes()
-        self.excitation_types = {}
-        self.symmetry = symmetry
-         
-        if symmetry == Symmetry.RADIAL:
-            assert self.mesh.points.shape[1] == 2 or np.all(self.mesh.points[:, 1] == 0.), \
-                "When symmetry is RADIAL, the geometry should lie in the XZ plane"
-    
-    def __str__(self):
-        return f'<Traceon Excitation,\n\t' \
-            + '\n\t'.join([f'{n}={v} ({t})' for n, (t, v) in self.excitation_types.items()]) \
-            + '>'
-     
-    def add_voltage(self, **kwargs):
-        """
-        Apply a fixed voltage to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the voltages in units of Volt. For example,
-            calling the function as `add_voltage(lens=50)` assigns a 50V value to the geometry elements part of the 'lens' physical group.
-            Alternatively, the value can be a function, which takes x, y, z coordinates as argument and returns the voltage at that position.
-            Note that in 2D symmetries (such as radial symmetry) the z value for this function will always be zero.
-        
-        """
-        for name, voltage in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            if isinstance(voltage, int) or isinstance(voltage, float):
-                self.excitation_types[name] = (ExcitationType.VOLTAGE_FIXED, voltage)
-            elif callable(voltage):
-                self.excitation_types[name] = (ExcitationType.VOLTAGE_FUN, voltage)
-            else:
-                raise NotImplementedError('Unrecognized voltage value')
-
-    def add_current(self, **kwargs):
-        """
-        Apply a fixed total current to the geometries assigned the given name. Note that a coil is assumed,
-        which implies that the current density is constant as a function of (r, z). In a solid piece of conducting material the current density would
-        be higher at small r (as the 'loop' around the axis is shorter and therefore the resistance is lower).
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the currents in units of Ampere. For example,
-            calling the function as `add_current(coild=10)` assigns a 10A value to the geometry elements part of the 'coil' physical group.
-        """
-
-        assert self.symmetry == Symmetry.RADIAL, "Currently magnetostatics are only supported for radially symmetric meshes"
-         
-        for name, current in kwargs.items():
-            assert name in self.mesh.physical_to_triangles.keys(), "Current can only be applied to a triangle electrode"
-            self.excitation_types[name] = (ExcitationType.CURRENT, current)
-
-    def has_current(self):
-        """Check whether a current is applied in this excitation."""
-        return any([t == ExcitationType.CURRENT for t, _ in self.excitation_types.values()])
-    
-    def is_electrostatic(self):
-        """Check whether the excitation contains electrostatic fields."""
-        return any([t in [ExcitationType.VOLTAGE_FIXED, ExcitationType.VOLTAGE_FUN] for t, _ in self.excitation_types.values()])
-     
-    def is_magnetostatic(self):
-        """Check whether the excitation contains magnetostatic fields."""
-        return any([t in [ExcitationType.MAGNETOSTATIC_POT, ExcitationType.CURRENT] for t, _ in self.excitation_types.values()])
-     
-    def add_magnetostatic_potential(self, **kwargs):
-        """
-        Apply a fixed magnetostatic potential to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the voltages in units of Ampere. For example,
-            calling the function as `add_magnetostatic_potential(lens=50)` assigns a 50A value to the geometry elements part of the 'lens' physical group.
-        """
-        for name, pot in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.MAGNETOSTATIC_POT, pot)
-
-    def add_magnetizable(self, **kwargs):
-        """
-        Assign a relative magnetic permeability to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example,
-            calling the function as `add_dielectric(spacer=2)` assign the relative dielectric constant of 2 to the `spacer` physical group.
-         
-        """
-
-        for name, permeability in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.MAGNETIZABLE, permeability)
-     
-    def add_dielectric(self, **kwargs):
-        """
-        Assign a dielectric constant to the geometries assigned the given name.
-        
-        Parameters
-        ----------
-        **kwargs : dict
-            The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example,
-            calling the function as `add_dielectric(spacer=2)` assign the relative dielectric constant of 2 to the `spacer` physical group.
-         
-        """
-        for name, permittivity in kwargs.items():
-            assert name in self.electrodes, f'Cannot add {name} to excitation, since it\'s not present in the mesh'
-            self.excitation_types[name] = (ExcitationType.DIELECTRIC, permittivity)
-
-    def add_electrostatic_boundary(self, *args):
-        """
-        Specify geometry elements as electrostatic boundary elements. At the boundary we require E·n = 0 at every point on the boundary. This
-        is equivalent to stating that the directional derivative of the electrostatic potential through the boundary is zero. Placing boundaries between
-        the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a dielectric with a dielectric
-        constant of zero. This is how a boundary is actually implemented internally.
-        
-        Parameters
-        ----------
-        *args: list of str
-            The geometry names that should be considered a boundary.
-        """
-        self.add_dielectric(**{a:0 for a in args})
-    
-    def add_magnetostatic_boundary(self, *args):
-        """
-        Specify geometry elements as magnetostatic boundary elements. At the boundary we require H·n = 0 at every point on the boundary. This
-        is equivalent to stating that the directional derivative of the magnetostatic potential through the boundary is zero. Placing boundaries between
-        the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a magnetic material with a magnetic 
-        permeability of zero. This is how a boundary is actually implemented internally.
-        
-        Parameters
-        ----------
-        *args: list of str
-            The geometry names that should be considered a boundary.
-        """
-
-        self.add_magnetizable(**{a:0 for a in args})
-    
-    def _split_for_superposition(self):
-        
-        # Names that have a fixed voltage excitation, not equal to 0.0
-        types = self.excitation_types
-        non_zero_fixed = [n for n, (t, v) in types.items() if t in [ExcitationType.VOLTAGE_FIXED,
-                                                                    ExcitationType.CURRENT] and v != 0.0]
-        
-        excitations = []
-         
-        for name in non_zero_fixed:
-             
-            new_types_dict = {}
-             
-            for n, (t, v) in types.items():
-                assert t != ExcitationType.VOLTAGE_FUN, "VOLTAGE_FUN excitation not supported for superposition."
-                 
-                if n == name:
-                    new_types_dict[n] = (t, 1.0)
-                elif t == ExcitationType.VOLTAGE_FIXED:
-                    new_types_dict[n] = (t, 0.0)
-                elif t == ExcitationType.CURRENT:
-                    new_types_dict[n] = (t, 0.0)
-                else:
-                    new_types_dict[n] = (t, v)
-            
-            exc = Excitation(self.mesh, self.symmetry)
-            exc.excitation_types = new_types_dict
-            excitations.append(exc)
-
-        assert len(non_zero_fixed) == len(excitations)
-        return {n:e for (n,e) in zip(non_zero_fixed, excitations)}
-
-    def _get_active_elements(self, type_):
-        assert type_ in ['electrostatic', 'magnetostatic']
-        
-        if self.symmetry == Symmetry.RADIAL:
-            elements = self.mesh.lines
-            physicals = self.mesh.physical_to_lines
-        else:
-            elements = self.mesh.triangles
-            physicals = self.mesh.physical_to_triangles
-
-        def type_check(excitation_type):
-            if type_ == 'electrostatic':
-                return excitation_type.is_electrostatic()
-            else:
-                return excitation_type in [ExcitationType.MAGNETIZABLE, ExcitationType.MAGNETOSTATIC_POT]
-        
-        inactive = np.full(len(elements), True)
-        for name, value in self.excitation_types.items():
-            if type_check(value[0]):
-                inactive[ physicals[name] ] = False
-         
-        map_index = np.arange(len(elements)) - np.cumsum(inactive)
-        names = {n:map_index[i] for n, i in physicals.items() \
-                    if n in self.excitation_types and type_check(self.excitation_types[n][0])}
-         
-        return self.mesh.points[ elements[~inactive] ], names
-     
-    def get_electrostatic_active_elements(self):
-        """Get elements in the mesh that have an electrostatic excitation
-        applied to them. 
-         
-        Returns
-        --------
-        A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. \
-        This array contains the vertices of the line elements or the triangles. \
-        Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line \
-        element is given by a polynomial interpolation of the points. \
-        names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, \
-        while the values are Numpy arrays of indices that can be used to index the points array.
-        """
-        return self._get_active_elements('electrostatic')
-    
-    def get_magnetostatic_active_elements(self):
-        """Get elements in the mesh that have an magnetostatic excitation
-        applied to them. This does not include current excitation, as these are not part of the matrix.
-    
-        Returns
-        --------
-        A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. \
-        This array contains the vertices of the line elements or the triangles. \
-        Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line \
-        element is given by a polynomial interpolation of the points. \
-        names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, \
-        while the values are Numpy arrays of indices that can be used to index the points array.
-        """
-
-        return self._get_active_elements('magnetostatic')
-
-

Methods

-
-
-def add_current(self, **kwargs) -
-
-

Apply a fixed total current to the geometries assigned the given name. Note that a coil is assumed, -which implies that the current density is constant as a function of (r, z). In a solid piece of conducting material the current density would -be higher at small r (as the 'loop' around the axis is shorter and therefore the resistance is lower).

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the currents in units of Ampere. For example, -calling the function as add_current(coild=10) assigns a 10A value to the geometry elements part of the 'coil' physical group.
-
-
-
-def add_dielectric(self, **kwargs) -
-
-

Assign a dielectric constant to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example, -calling the function as add_dielectric(spacer=2) assign the relative dielectric constant of 2 to the spacer physical group.
-
-
-
-def add_electrostatic_boundary(self, *args) -
-
-

Specify geometry elements as electrostatic boundary elements. At the boundary we require E·n = 0 at every point on the boundary. This -is equivalent to stating that the directional derivative of the electrostatic potential through the boundary is zero. Placing boundaries between -the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a dielectric with a dielectric -constant of zero. This is how a boundary is actually implemented internally.

-

Parameters

-
-
*args : list of str
-
The geometry names that should be considered a boundary.
-
-
-
-def add_magnetizable(self, **kwargs) -
-
-

Assign a relative magnetic permeability to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the relative dielectric constants. For example, -calling the function as add_dielectric(spacer=2) assign the relative dielectric constant of 2 to the spacer physical group.
-
-
-
-def add_magnetostatic_boundary(self, *args) -
-
-

Specify geometry elements as magnetostatic boundary elements. At the boundary we require H·n = 0 at every point on the boundary. This -is equivalent to stating that the directional derivative of the magnetostatic potential through the boundary is zero. Placing boundaries between -the spaces of electrodes usually helps convergence tremendously. Note that a boundary is equivalent to a magnetic material with a magnetic -permeability of zero. This is how a boundary is actually implemented internally.

-

Parameters

-
-
*args : list of str
-
The geometry names that should be considered a boundary.
-
-
-
-def add_magnetostatic_potential(self, **kwargs) -
-
-

Apply a fixed magnetostatic potential to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the voltages in units of Ampere. For example, -calling the function as add_magnetostatic_potential(lens=50) assigns a 50A value to the geometry elements part of the 'lens' physical group.
-
-
-
-def add_voltage(self, **kwargs) -
-
-

Apply a fixed voltage to the geometries assigned the given name.

-

Parameters

-
-
**kwargs : dict
-
The keys of the dictionary are the geometry names, while the values are the voltages in units of Volt. For example, -calling the function as add_voltage(lens=50) assigns a 50V value to the geometry elements part of the 'lens' physical group. -Alternatively, the value can be a function, which takes x, y, z coordinates as argument and returns the voltage at that position. -Note that in 2D symmetries (such as radial symmetry) the z value for this function will always be zero.
-
-
-
-def get_electrostatic_active_elements(self) -
-
-

Get elements in the mesh that have an electrostatic excitation -applied to them.

-

Returns

-

A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. -This array contains the vertices of the line elements or the triangles. -Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line -element is given by a polynomial interpolation of the points. -names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, -while the values are Numpy arrays of indices that can be used to index the points array.

-
-
-def get_magnetostatic_active_elements(self) -
-
-

Get elements in the mesh that have an magnetostatic excitation -applied to them. This does not include current excitation, as these are not part of the matrix.

-

Returns

-

A tuple of two elements: (points, names). points is a Numpy array of shape (N, 4, 3) in the case of 2D and (N, 3, 3) in the case of 3D. -This array contains the vertices of the line elements or the triangles. -Multiple points per line elements are used in the case of 2D since higher order BEM is employed, in which the true position on the line -element is given by a polynomial interpolation of the points. -names is a dictionary, the keys being the names of the physical groups mentioned by this excitation, -while the values are Numpy arrays of indices that can be used to index the points array.

-
-
-def has_current(self) -
-
-

Check whether a current is applied in this excitation.

-
-
-def is_electrostatic(self) -
-
-

Check whether the excitation contains electrostatic fields.

-
-
-def is_magnetostatic(self) -
-
-

Check whether the excitation contains magnetostatic fields.

-
-
-
-
-class ExcitationType -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Possible excitation that can be applied to elements of the geometry. See the methods of Excitation for documentation.

-
- -Expand source code - -
class ExcitationType(IntEnum):
-    """Possible excitation that can be applied to elements of the geometry. See the methods of `Excitation` for documentation."""
-    VOLTAGE_FIXED = 1
-    VOLTAGE_FUN = 2
-    DIELECTRIC = 3
-     
-    CURRENT = 4
-    MAGNETOSTATIC_POT = 5
-    MAGNETIZABLE = 6
-     
-    def is_electrostatic(self):
-        return self in [ExcitationType.VOLTAGE_FIXED,
-                        ExcitationType.VOLTAGE_FUN,
-                        ExcitationType.DIELECTRIC]
-
-    def is_magnetostatic(self):
-        return self in [ExcitationType.MAGNETOSTATIC_POT,
-                        ExcitationType.MAGNETIZABLE,
-                        ExcitationType.CURRENT]
-     
-    def __str__(self):
-        if self == ExcitationType.VOLTAGE_FIXED:
-            return 'voltage fixed'
-        elif self == ExcitationType.VOLTAGE_FUN:
-            return 'voltage function'
-        elif self == ExcitationType.DIELECTRIC:
-            return 'dielectric'
-        elif self == ExcitationType.CURRENT:
-            return 'current'
-        elif self == ExcitationType.MAGNETOSTATIC_POT:
-            return 'magnetostatic potential'
-        elif self == ExcitationType.MAGNETIZABLE:
-            return 'magnetizable'
-         
-        raise RuntimeError('ExcitationType not understood in __str__ method')
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var CURRENT
-
-
-
-
var DIELECTRIC
-
-
-
-
var MAGNETIZABLE
-
-
-
-
var MAGNETOSTATIC_POT
-
-
-
-
var VOLTAGE_FIXED
-
-
-
-
var VOLTAGE_FUN
-
-
-
-
-

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-
-
-class Symmetry -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Symmetry to be used for solver. Used when deciding which formulas to use in the Boundary Element Method. The currently -supported symmetries are radial symmetry (also called cylindrical symmetry) and general 3D geometries.

-
- -Expand source code - -
class Symmetry(IntEnum):
-    """Symmetry to be used for solver. Used when deciding which formulas to use in the Boundary Element Method. The currently
-    supported symmetries are radial symmetry (also called cylindrical symmetry) and general 3D geometries.
-    """
-    RADIAL = 0
-    THREE_D = 2
-    
-    def __str__(self):
-        if self == Symmetry.RADIAL:
-            return 'radial'
-        elif self == Symmetry.THREE_D:
-            return '3d' 
-    
-    def is_2d(self):
-        return self == Symmetry.RADIAL
-        
-    def is_3d(self):
-        return self == Symmetry.THREE_D
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var RADIAL
-
-
-
-
var THREE_D
-
-
-
-
-

Methods

-
-
-def is_2d(self) -
-
-
-
-
-def is_3d(self) -
-
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/focus.html b/docs/docs/v0.7.0rc3/focus.html deleted file mode 100644 index 32981b8e..00000000 --- a/docs/docs/v0.7.0rc3/focus.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - - -traceon.focus API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.focus

-
-
-

Module containing a single function to find the focus of a beam of electron trajecories.

-
-
-
-
-
-
-

Functions

-
-
-def focus_position(positions) -
-
-

Find the focus of the given trajectories (which are returned from Tracer.__call__()). -The focus is found using a least square method by considering the final positions and velocities of -the given trajectories and linearly extending the trajectories backwards.

-

Parameters

-
-
positions : iterable of (N,6) np.ndarray float64
-
Trajectories of electrons, as returned by Tracer.__call__()
-
-

Returns

-

(3,) np.ndarray of float64, representing the position of the focus

-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/interpolation.html b/docs/docs/v0.7.0rc3/interpolation.html deleted file mode 100644 index 15207341..00000000 --- a/docs/docs/v0.7.0rc3/interpolation.html +++ /dev/null @@ -1,374 +0,0 @@ - - - - - - -traceon.interpolation API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.interpolation

-
-
-
-
-
-
-
-
-
-
-

Classes

-
-
-class FieldAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-

An electrostatic field resulting from a radial series expansion around the optical axis. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the axial_derivative_interpolation methods. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldAxial(S.Field, ABC):
-    """An electrostatic field resulting from a radial series expansion around the optical axis. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `axial_derivative_interpolation` methods. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        N = len(z)
-        assert z.shape == (N,)
-        assert electrostatic_coeffs is None or len(electrostatic_coeffs)== N-1
-        assert magnetostatic_coeffs is None or len(magnetostatic_coeffs) == N-1
-        assert electrostatic_coeffs is not None or magnetostatic_coeffs is not None
-        
-        assert z[0] < z[-1], "z values in axial interpolation should be ascending"
-         
-        self.z = z
-        self.electrostatic_coeffs = electrostatic_coeffs if electrostatic_coeffs is not None else np.zeros_like(magnetostatic_coeffs)
-        self.magnetostatic_coeffs = magnetostatic_coeffs if magnetostatic_coeffs is not None else np.zeros_like(electrostatic_coeffs)
-        
-        self.has_electrostatic = np.any(self.electrostatic_coeffs != 0.)
-        self.has_magnetostatic = np.any(self.magnetostatic_coeffs != 0.)
-     
-    def is_electrostatic(self):
-        return self.has_electrostatic
-
-    def is_magnetostatic(self):
-        return self.has_magnetostatic
-     
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}, zmin={self.z[0]} mm, zmax={self.z[-1]} mm,\n\tNumber of samples on optical axis: {len(self.z)}>'
-     
-    def __add__(self, other):
-        if isinstance(other, FieldAxial):
-            assert np.array_equal(self.z, other.z), "Cannot add FieldAxial if optical axis sampling is different."
-            assert self.electrostatic_coeffs.shape == other.electrostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            assert self.magnetostatic_coeffs.shape == other.magnetostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            return self.__class__(self.z, self.electrostatic_coeffs+other.electrostatic_coeffs, self.magnetostatic_coeffs + other.magnetostatic_coeffs)
-         
-        return NotImplemented
-    
-    def __sub__(self, other):
-        return self.__add__(-other)
-    
-    def __radd__(self, other):
-        return self.__add__(other)
-     
-    def __mul__(self, other):
-        if isinstance(other, int) or isinstance(other, float):
-            return self.__class__(self.z, other*self.electrostatic_coeffs, other*self.magnetostatic_coeffs)
-         
-        return NotImplemented
-    
-    def __neg__(self):
-        return -1*self
-    
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-

Inherited members

- -
-
-class FieldRadialAxial -(field, zmin, zmax, N=None) -
-
-
-
- -Expand source code - -
class FieldRadialAxial(FieldAxial):
-    """ """
-    def __init__(self, field, zmin, zmax, N=None):
-        assert isinstance(field, S.FieldRadialBEM)
-
-        z, electrostatic_coeffs, magnetostatic_coeffs = FieldRadialAxial._get_interpolation_coefficients(field, zmin, zmax, N=N)
-        
-        super().__init__(z, electrostatic_coeffs, magnetostatic_coeffs)
-        
-        assert self.electrostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        assert self.magnetostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-    
-    @staticmethod
-    def _get_interpolation_coefficients(field: S.FieldRadialBEM, zmin, zmax, N=None):
-        assert zmax > zmin, "zmax should be bigger than zmin"
-
-        N_charges = max(len(field.electrostatic_point_charges.charges), len(field.magnetostatic_point_charges.charges))
-        N = N if N is not None else int(FACTOR_AXIAL_DERIV_SAMPLING_2D*N_charges)
-        z = np.linspace(zmin, zmax, N)
-        
-        st = time.time()
-        elec_derivs = np.concatenate(util.split_collect(field.get_electrostatic_axial_potential_derivatives, z), axis=0)
-        elec_coeffs = _quintic_spline_coefficients(z, elec_derivs.T)
-        
-        mag_derivs = np.concatenate(util.split_collect(field.get_magnetostatic_axial_potential_derivatives, z), axis=0)
-        mag_coeffs = _quintic_spline_coefficients(z, mag_derivs.T)
-        
-        logging.log_info(f'Computing derivative interpolation took {(time.time()-st)*1000:.2f} ms ({len(z)} items)')
-
-        return z, elec_coeffs, mag_coeffs
-     
-    def electrostatic_field_at_point(self, point_):
-        """
-        Compute the electric field, \\( \\vec{E} = -\\nabla \\phi \\)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        Numpy array containing the field strengths (in units of V/mm) in the r and z directions.
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.field_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_field_at_point(self, point_):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.field_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-     
-    def electrostatic_potential_at_point(self, point_):
-        """
-        Compute the electrostatic potential (close to the axis).
-
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the potential.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.potential_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_potential_at_point(self, point_):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\)) close to the axis
-        
-        Parameters
-        ----------
-        point: (2,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.potential_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialAxial(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def electrostatic_field_at_point(self, point_) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Numpy array containing the field strengths (in units of V/mm) in the r and z directions.

-
-
-def electrostatic_potential_at_point(self, point_) -
-
-

Compute the electrostatic potential (close to the axis).

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the potential.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point_) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(2,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point_) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi ) close to the axis

-

Parameters

-
-
point : (2,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/logging.html b/docs/docs/v0.7.0rc3/logging.html deleted file mode 100644 index 7142c272..00000000 --- a/docs/docs/v0.7.0rc3/logging.html +++ /dev/null @@ -1,148 +0,0 @@ - - - - - - -traceon.logging API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.logging

-
-
-
-
-
-
-
-
-

Functions

-
-
-def set_log_level(level) -
-
-

Set the current LogLevel. Note that the log level can also -be set by setting the environment value TRACEON_LOG_LEVEL to one -of 'debug', 'info', 'warning', 'error' or 'silent'.

-
-
-
-
-

Classes

-
-
-class LogLevel -(value, names=None, *, module=None, qualname=None, type=None, start=1) -
-
-

Enumeration representing a certain verbosity of logging.

-
- -Expand source code - -
class LogLevel(IntEnum):
-    """Enumeration representing a certain verbosity of logging."""
-    
-    DEBUG = 0
-    """Print debug, info, warning and error information."""
-
-    INFO = 1
-    """Print info, warning and error information."""
-
-    WARNING = 2
-    """Print only warnings and errors."""
-
-    ERROR = 3
-    """Print only errors."""
-     
-    SILENT = 4
-    """Do not print anything."""
-
-

Ancestors

-
    -
  • enum.IntEnum
  • -
  • builtins.int
  • -
  • enum.Enum
  • -
-

Class variables

-
-
var DEBUG
-
-

Print debug, info, warning and error information.

-
-
var ERROR
-
-

Print only errors.

-
-
var INFO
-
-

Print info, warning and error information.

-
-
var SILENT
-
-

Do not print anything.

-
-
var WARNING
-
-

Print only warnings and errors.

-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/plotting.html b/docs/docs/v0.7.0rc3/plotting.html deleted file mode 100644 index a38e6723..00000000 --- a/docs/docs/v0.7.0rc3/plotting.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - -traceon.plotting API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.plotting

-
-
-

The traceon.plotting module uses the vedo plotting library to provide some convenience functions -to show the line and triangle meshes generated by Traceon.

-
-
-
-
-
-
-

Functions

-
-
-def plot_mesh(mesh, show_normals=False, show_legend=True, **colors) -
-
-

Plot mesh using the Vedo library. Optionally showing normal vectors.

-

Parameters

-
-
show_normals : bool
-
Whether to show the normal vectors at every element
-
show_legend : bool
-
Whether to show the legend
-
colors : dict of (string, string)
-
Use keyword arguments to specify colors, for example plot_mesh(mesh, lens='blue', ground='green')
-
-
-
-
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/solver.html b/docs/docs/v0.7.0rc3/solver.html deleted file mode 100644 index 6c969d64..00000000 --- a/docs/docs/v0.7.0rc3/solver.html +++ /dev/null @@ -1,1397 +0,0 @@ - - - - - - -traceon.solver API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.solver

-
-
-

The solver module uses the Boundary Element Method (BEM) to compute the surface charge distribution of a given -geometry and excitation. Once the surface charge distribution is known, the field at any arbitrary position in space -can be calculated by integration over the charged boundary. However, doing a field evaluation in this manner is very slow -as for every field evaluation an iteration needs to be done over all elements in the mesh. Especially for particle tracing it -is crucial that the field evaluation can be done faster. To achieve this, interpolation techniques can be used.

-

The solver package offers interpolation in the form of radial series expansions to drastically increase the speed of ray tracing. For -this consider the axial_derivative_interpolation methods documented below.

-

Radial series expansion in cylindrical symmetry

-

Let \phi_0(z) be the potential along the optical axis. We can express the potential around the optical axis as:

-

-\phi = \phi_0(z_0) - \frac{r^2}{4} \frac{\partial \phi_0^2}{\partial z^2} + \frac{r^4}{64} \frac{\partial^4 \phi_0}{\partial z^4} - \frac{r^6}{2304} \frac{\partial \phi_0^6}{\partial z^6} + \cdots -

-

Therefore, if we can efficiently compute the axial potential derivatives \frac{\partial \phi_0^n}{\partial z^n} we can compute the potential and therefore the fields around the optical axis. -For the derivatives of \phi_0(z) closed form formulas exist in the case of radially symmetric geometries, see for example formula 13.16a in [1]. Traceon uses a recursive version of these formulas to -very efficiently compute the axial derivatives of the potential.

-

Radial series expansion in 3D

-

In a general three dimensional geometry the potential will be dependent not only on the distance from the optical axis but also on the angle \theta around the optical axis -at which the potential is sampled. It turns out (equation (35, 24) in [2]) the potential can be written as follows:

-

-\phi = \sum_{\nu=0}^\infty \sum_{m=0}^\infty r^{2\nu + m} \left( A^\nu_m \cos(m\theta) + B^\nu_m \sin(m\theta) \right) -

-

The A^\nu_m and B^\nu_m coefficients can be expressed in directional derivatives perpendicular to the optical axis, analogous to the radial symmetric case. The -mathematics of calculating these coefficients quickly and accurately gets quite involved, but all details have been abstracted away from the user.

-

References

-

[1] P. Hawkes, E. Kasper. Principles of Electron Optics. Volume one: Basic Geometrical Optics. 2018.

-

[2] W. Glaser. Grundlagen der Elektronenoptik. 1952.

-
-
-
-
-
-
-

Functions

-
-
-def solve_direct(excitation) -
-
-

Solve for the charges on the surface of the geometry by using a direct method and taking -into account the specified excitation.

-

Parameters

-
-
excitation : Excitation
-
The excitation that produces the resulting field.
-
-

Returns

-

A FieldRadialBEM if the geometry (contained in the given excitation) is radially symmetric. If the geometry is a three -dimensional geometry Field3D_BEM is returned.

-
-
-def solve_direct_superposition(excitation) -
-
-

superposition : bool -When using superposition the function returns multiple fields. Each field corresponds with a unity excitation (1V) -of a physical group that was previously assigned a non-zero fixed voltage value. This is useful when a geometry needs -to be analyzed for many different voltage settings. In this case taking a linear superposition of the returned fields -allows to select a different voltage 'setting' without inducing any computational cost. There is no computational cost -involved in using superposition=True since a direct solver is used which easily allows for multiple right hand sides (the -matrix does not have to be inverted multiple times). However, voltage functions are invalid in the superposition process (position dependent voltages).

-
-
-
-
-

Classes

-
-
-class Field -
-
-

Helper class that provides a standard way to create an ABC using -inheritance.

-
- -Expand source code - -
class Field(ABC):
-    def field_at_point(self, point):
-        """Convenience function for getting the field in the case that the field is purely electrostatic
-        or magneotstatic. Automatically picks one of `electrostatic_field_at_point` or `magnetostatic_field_at_point`.
-        Throws an exception when the field is both electrostatic and magnetostatic.
-
-        Parameters
-        ---------------------
-        point: (3,) np.ndarray of float64
-
-        Returns
-        --------------------
-        (3,) np.ndarray of float64. The electrostatic field \\(\\vec{E}\\) or the magnetostatic field \\(\\vec{H}\\).
-        """
-        elec, mag = self.is_electrostatic(), self.is_magnetostatic()
-        
-        if elec and not mag:
-            return self.electrostatic_field_at_point(point)
-        elif not elec and mag:
-            return self.magnetostatic_field_at_point(point)
-         
-        raise RuntimeError("Cannot use field_at_point when both electric and magnetic fields are present, " \
-            "use electrostatic_field_at_point or magnetostatic_potential_at_point")
-     
-    def potential_at_point(self, point):
-        """Convenience function for getting the potential in the case that the field is purely electrostatic
-        or magneotstatic. Automatically picks one of `electrostatic_potential_at_point` or `magnetostatic_potential_at_point`.
-        Throws an exception when the field is both electrostatic and magnetostatic.
-         
-        Parameters
-        ---------------------
-        point: (3,) np.ndarray of float64
-
-        Returns
-        --------------------
-        float. The electrostatic potential (unit Volt) or magnetostaic scalar potential (unit Ampere)
-        """
-        elec, mag = self.is_electrostatic(), self.is_magnetostatic()
-         
-        if elec and not mag:
-            return self.electrostatic_potential_at_point(point)
-        elif not elec and mag:
-            return self.magnetostatic_potential_at_point(point)
-         
-        raise RuntimeError("Cannot use potential_at_point when both electric and magnetic fields are present, " \
-            "use electrostatic_potential_at_point or magnetostatic_potential_at_point")
-
-    @abstractmethod
-    def is_electrostatic(self):
-        ...
-    
-    @abstractmethod
-    def is_magnetostatic(self):
-        ...
-    
-    @abstractmethod
-    def magnetostatic_potential_at_point(self, point):
-        ...
-    
-    @abstractmethod
-    def electrostatic_potential_at_point(self, point):
-        ...
-    
-    @abstractmethod
-    def magnetostatic_field_at_point(self, point):
-        ...
-    
-    @abstractmethod
-    def electrostatic_field_at_point(self, point):
-        ...
-
-

Ancestors

-
    -
  • abc.ABC
  • -
-

Subclasses

- -

Methods

-
-
-def electrostatic_field_at_point(self, point) -
-
-
-
-
-def electrostatic_potential_at_point(self, point) -
-
-
-
-
-def field_at_point(self, point) -
-
-

Convenience function for getting the field in the case that the field is purely electrostatic -or magneotstatic. Automatically picks one of electrostatic_field_at_point or magnetostatic_field_at_point. -Throws an exception when the field is both electrostatic and magnetostatic.

-

Parameters

-
-
point : (3,) np.ndarray of float64
-
 
-
-

Returns

-

(3,) np.ndarray of float64. The electrostatic field \vec{E} or the magnetostatic field \vec{H}.

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-def magnetostatic_field_at_point(self, point) -
-
-
-
-
-def magnetostatic_potential_at_point(self, point) -
-
-
-
-
-def potential_at_point(self, point) -
-
-

Convenience function for getting the potential in the case that the field is purely electrostatic -or magneotstatic. Automatically picks one of electrostatic_potential_at_point or magnetostatic_potential_at_point. -Throws an exception when the field is both electrostatic and magnetostatic.

-

Parameters

-
-
point : (3,) np.ndarray of float64
-
 
-
-

Returns

-
-
float. The electrostatic potential (unit Volt) or magnetostaic scalar potential (unit Ampere)
-
 
-
-
-
-
-
-class Field3D_BEM -(electrostatic_point_charges=None, magnetostatic_point_charges=None) -
-
-

An electrostatic field resulting from a general 3D geometry. The field is a result of the surface charges as computed by the -solve_direct() function. See the comments in FieldBEM.

-
- -Expand source code - -
class Field3D_BEM(FieldBEM):
-    """An electrostatic field resulting from a general 3D geometry. The field is a result of the surface charges as computed by the
-    `solve_direct` function. See the comments in `FieldBEM`."""
-     
-    def __init__(self, electrostatic_point_charges=None, magnetostatic_point_charges=None):
-        
-        if electrostatic_point_charges is None:
-            electrostatic_point_charges = EffectivePointCharges.empty_3d()
-        if magnetostatic_point_charges is None:
-            magnetostatic_point_charges = EffectivePointCharges.empty_3d()
-         
-        super().__init__(electrostatic_point_charges, magnetostatic_point_charges, EffectivePointCharges.empty_3d())
-        
-        self.symmetry = E.Symmetry.THREE_D
-
-        for eff in [electrostatic_point_charges, magnetostatic_point_charges]:
-            N = len(eff.charges)
-            assert eff.charges.shape == (N,)
-            assert eff.jacobians.shape == (N, backend.N_TRIANGLE_QUAD)
-            assert eff.positions.shape == (N, backend.N_TRIANGLE_QUAD, 3)
-     
-    def electrostatic_field_at_point(self, point_):
-        """
-        Compute the electric field, \\( \\vec{E} = -\\nabla \\phi \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) array of float64 representing the electric field 
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.field_3d(point, charges, jacobians, positions)
-     
-    def electrostatic_potential_at_point(self, point_):
-        """
-        Compute the electrostatic potential.
-
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.potential_3d(point, charges, jacobians, positions)
-     
-    def magnetostatic_field_at_point(self, point_):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.field_3d(point, charges, jacobians, positions)
-     
-    def magnetostatic_potential_at_point(self, point_):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\))
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.potential_3d(point, charges, jacobians, positions)
-     
-    def area_of_element(self, i):
-        jacobians = self.electrostatic_point_charges.jacobians
-        return np.sum(jacobians[i])
-    
-    def get_tracer(self, bounds):
-        return T.Tracer3D_BEM(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def area_of_element(self, i) -
-
-
-
-
-def electrostatic_field_at_point(self, point_) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) array of float64 representing the electric field

-
-
-def electrostatic_potential_at_point(self, point_) -
-
-

Compute the electrostatic potential.

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point_) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point_) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi )

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-class FieldAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-

An electrostatic field resulting from a radial series expansion around the optical axis. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the axial_derivative_interpolation methods. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldAxial(Field):
-    """An electrostatic field resulting from a radial series expansion around the optical axis. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `axial_derivative_interpolation` methods. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        N = len(z)
-        assert z.shape == (N,)
-        assert electrostatic_coeffs is None or len(electrostatic_coeffs)== N-1
-        assert magnetostatic_coeffs is None or len(magnetostatic_coeffs) == N-1
-        assert electrostatic_coeffs is not None or magnetostatic_coeffs is not None
-        
-        assert z[0] < z[-1], "z values in axial interpolation should be ascending"
-         
-        self.z = z
-        self.electrostatic_coeffs = electrostatic_coeffs if electrostatic_coeffs is not None else np.zeros_like(magnetostatic_coeffs)
-        self.magnetostatic_coeffs = magnetostatic_coeffs if magnetostatic_coeffs is not None else np.zeros_like(electrostatic_coeffs)
-        
-        self.has_electrostatic = np.any(self.electrostatic_coeffs != 0.)
-        self.has_magnetostatic = np.any(self.magnetostatic_coeffs != 0.)
-     
-    def is_electrostatic(self):
-        return self.has_electrostatic
-
-    def is_magnetostatic(self):
-        return self.has_magnetostatic
-     
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}, zmin={self.z[0]} mm, zmax={self.z[-1]} mm,\n\tNumber of samples on optical axis: {len(self.z)}>'
-     
-    def __add__(self, other):
-        if isinstance(other, FieldAxial):
-            assert np.array_equal(self.z, other.z), "Cannot add FieldAxial if optical axis sampling is different."
-            assert self.electrostatic_coeffs.shape == other.electrostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            assert self.magnetostatic_coeffs.shape == other.magnetostatic_coeffs.shape, "Cannot add FieldAxial if shape of axial coefficients is unequal."
-            return self.__class__(self.z, self.electrostatic_coeffs+other.electrostatic_coeffs, self.magnetostatic_coeffs + other.magnetostatic_coeffs)
-         
-        return NotImplemented
-    
-    def __sub__(self, other):
-        return self.__add__(-other)
-    
-    def __radd__(self, other):
-        return self.__add__(other)
-     
-    def __mul__(self, other):
-        if isinstance(other, int) or isinstance(other, float):
-            return self.__class__(self.z, other*self.electrostatic_coeffs, other*self.magnetostatic_coeffs)
-         
-        return NotImplemented
-    
-    def __neg__(self):
-        return -1*self
-    
-    def __rmul__(self, other):
-        return self.__mul__(other)
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-

Inherited members

- -
-
-class FieldBEM -(electrostatic_point_charges, magnetostatic_point_charges, current_point_charges) -
-
-

An electrostatic field (resulting from surface charges) as computed from the Boundary Element Method. You should -not initialize this class yourself, but it is used as a base class for the fields returned by the solve_direct() function. -This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields.

-
- -Expand source code - -
class FieldBEM(Field, ABC):
-    """An electrostatic field (resulting from surface charges) as computed from the Boundary Element Method. You should
-    not initialize this class yourself, but it is used as a base class for the fields returned by the `solve_direct` function. 
-    This base class overloads the +,*,- operators so it is very easy to take a superposition of different fields."""
-    
-    def __init__(self, electrostatic_point_charges, magnetostatic_point_charges, current_point_charges):
-        assert all([isinstance(eff, EffectivePointCharges) for eff in [electrostatic_point_charges,
-                                                                       magnetostatic_point_charges,
-                                                                       current_point_charges]])
-        self.electrostatic_point_charges = electrostatic_point_charges
-        self.magnetostatic_point_charges = magnetostatic_point_charges
-        self.current_point_charges = current_point_charges
-        self.field_bounds = None
-     
-    def set_bounds(self, bounds):
-        """Set the field bounds. Outside the field bounds the field always returns zero (i.e. no field). Note
-        that even in 2D the field bounds needs to be specified for x,y and z axis. The trajectories in the presence
-        of magnetostatic field are in general 3D even in radial symmetric geometries.
-        
-        Parameters
-        -------------------
-        bounds: (3, 2) np.ndarray of float64
-            The min, max value of x, y, z respectively within the field is still computed.
-        """
-        self.field_bounds = np.array(bounds)
-        assert self.field_bounds.shape == (3,2)
-    
-    def is_electrostatic(self):
-        return len(self.electrostatic_point_charges) > 0
-
-    def is_magnetostatic(self):
-        return len(self.magnetostatic_point_charges) > 0 or len(self.current_point_charges) > 0 
-     
-    def __add__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__add__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__add__(other.magnetostatic_point_charges),
-            self.current_point_charges.__add__(other.current_point_charges))
-     
-    def __sub__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__sub__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__sub__(other.magnetostatic_point_charges),
-            self.current_point_charges.__sub__(other.current_point_charges))
-    
-    def __radd__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__radd__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__radd__(other.magnetostatic_point_charges),
-            self.current_point_charges.__radd__(other.current_point_charges))
-    
-    def __mul__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__mul__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__mul__(other.magnetostatic_point_charges),
-            self.current_point_charges.__mul__(other.current_point_charges))
-    
-    def __neg__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__neg__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__neg__(other.magnetostatic_point_charges),
-            self.current_point_charges.__neg__(other.current_point_charges))
-     
-    def __rmul__(self, other):
-        return self.__class__(
-            self.electrostatic_point_charges.__rmul__(other.electrostatic_point_charges),
-            self.magnetostatic_point_charges.__rmul__(other.magnetostatic_point_charges),
-            self.current_point_charges.__rmul__(other.current_point_charges))
-     
-    def area_of_elements(self, indices):
-        """Compute the total area of the elements at the given indices.
-        
-        Parameters
-        ------------
-        indices: int iterable
-            Indices giving which elements to include in the area calculation.
-
-        Returns
-        ---------------
-        The sum of the area of all elements with the given indices.
-        """
-        return sum(self.area_of_element(i) for i in indices) 
-
-    @abstractmethod
-    def area_of_element(self, i: int) -> float:
-        ...
-    
-    def charge_on_element(self, i):
-        return self.area_of_element(i) * self.electrostatic_point_charges.charges[i]
-    
-    def charge_on_elements(self, indices):
-        """Compute the sum of the charges present on the elements with the given indices. To
-        get the total charge of a physical group use `names['name']` for indices where `names` 
-        is returned by `traceon.excitation.Excitation.get_electrostatic_active_elements()`.
-
-        Parameters
-        ----------
-        indices: (N,) array of int
-            indices of the elements contributing to the charge sum. 
-         
-        Returns
-        -------
-        The sum of the charge. See the note about units on the front page."""
-        return sum(self.charge_on_element(i) for i in indices)
-    
-    def __str__(self):
-        name = self.__class__.__name__
-        return f'<Traceon {name}\n' \
-            f'\tNumber of electrostatic points: {len(self.electrostatic_point_charges)}\n' \
-            f'\tNumber of magnetizable points: {len(self.magnetostatic_point_charges)}\n' \
-            f'\tNumber of current rings: {len(self.current_point_charges)}>'
-
-

Ancestors

- -

Subclasses

- -

Methods

-
-
-def area_of_element(self, i: int) ‑> float -
-
-
-
-
-def area_of_elements(self, indices) -
-
-

Compute the total area of the elements at the given indices.

-

Parameters

-
-
indices : int iterable
-
Indices giving which elements to include in the area calculation.
-
-

Returns

-

The sum of the area of all elements with the given indices.

-
-
-def charge_on_element(self, i) -
-
-
-
-
-def charge_on_elements(self, indices) -
-
-

Compute the sum of the charges present on the elements with the given indices. To -get the total charge of a physical group use names['name'] for indices where names -is returned by Excitation.get_electrostatic_active_elements().

-

Parameters

-
-
indices : (N,) array of int
-
indices of the elements contributing to the charge sum.
-
-

Returns

-

The sum of the charge. See the note about units on the front page.

-
-
-def is_electrostatic(self) -
-
-
-
-
-def is_magnetostatic(self) -
-
-
-
-
-def set_bounds(self, bounds) -
-
-

Set the field bounds. Outside the field bounds the field always returns zero (i.e. no field). Note -that even in 2D the field bounds needs to be specified for x,y and z axis. The trajectories in the presence -of magnetostatic field are in general 3D even in radial symmetric geometries.

-

Parameters

-
-
bounds : (3, 2) np.ndarray of float64
-
The min, max value of x, y, z respectively within the field is still computed.
-
-
-
-

Inherited members

- -
-
-class FieldRadialAxial -(z, electrostatic_coeffs=None, magnetostatic_coeffs=None) -
-
-
-
- -Expand source code - -
class FieldRadialAxial(FieldAxial):
-    """ """
-    def __init__(self, z, electrostatic_coeffs=None, magnetostatic_coeffs=None):
-        super().__init__(z, electrostatic_coeffs, magnetostatic_coeffs)
-        assert self.electrostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        assert self.magnetostatic_coeffs.shape == (len(z)-1, backend.DERIV_2D_MAX, 6)
-        self.symmetry = E.Symmetry.RADIAL
-    
-    def electrostatic_field_at_point(self, point_):
-        """
-        Compute the electric field, \\( \\vec{E} = -\\nabla \\phi \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) array of float64, containing the field strengths (units of V/m)
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.field_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_field_at_point(self, point_):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) array of float64, containing the field strengths (units of A/m)
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.field_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-     
-    def electrostatic_potential_at_point(self, point_):
-        """
-        Compute the electrostatic potential (close to the axis).
-
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the potential.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.potential_radial_derivs(point, self.z, self.electrostatic_coeffs)
-    
-    def magnetostatic_potential_at_point(self, point_):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\)) close to the axis
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        return backend.potential_radial_derivs(point, self.z, self.magnetostatic_coeffs)
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialAxial(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def electrostatic_field_at_point(self, point_) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) array of float64, containing the field strengths (units of V/m)

-
-
-def electrostatic_potential_at_point(self, point_) -
-
-

Compute the electrostatic potential (close to the axis).

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the potential.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point_) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) array of float64, containing the field strengths (units of A/m)

-
-
-def magnetostatic_potential_at_point(self, point_) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi ) close to the axis

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-class FieldRadialBEM -(electrostatic_point_charges=None, magnetostatic_point_charges=None, current_point_charges=None) -
-
-

A radially symmetric electrostatic field. The field is a result of the surface charges as computed by the -solve_direct() function. See the comments in FieldBEM.

-
- -Expand source code - -
class FieldRadialBEM(FieldBEM):
-    """A radially symmetric electrostatic field. The field is a result of the surface charges as computed by the
-    `solve_direct` function. See the comments in `FieldBEM`."""
-    
-    def __init__(self, electrostatic_point_charges=None, magnetostatic_point_charges=None, current_point_charges=None):
-        if electrostatic_point_charges is None:
-            electrostatic_point_charges = EffectivePointCharges.empty_2d()
-        if magnetostatic_point_charges is None:
-            magnetostatic_point_charges = EffectivePointCharges.empty_2d()
-        if current_point_charges is None:
-            current_point_charges = EffectivePointCharges.empty_3d()
-         
-        self.symmetry = E.Symmetry.RADIAL
-        super().__init__(electrostatic_point_charges, magnetostatic_point_charges, current_point_charges)
-         
-    def current_field_at_point(self, point_):
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-            
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-        return backend.current_field(point, currents, jacobians, positions)
-     
-    def electrostatic_field_at_point(self, point_):
-        """
-        Compute the electric field, \\( \\vec{E} = -\\nabla \\phi \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        (3,) array of float64, containing the field strengths (units of V/m)
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-          
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.field_radial(point, charges, jacobians, positions)
-     
-    def electrostatic_potential_at_point(self, point_):
-        """
-        Compute the electrostatic potential.
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of V).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.potential_radial(point, charges, jacobians, positions)
-    
-    def magnetostatic_field_at_point(self, point_):
-        """
-        Compute the magnetic field \\( \\vec{H} \\)
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-             
-        Returns
-        -------
-        (3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        current_field = self.current_field_at_point(point)
-        
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        
-        mag_field = backend.field_radial(point, charges, jacobians, positions)
-
-        return current_field + mag_field
-
-    def magnetostatic_potential_at_point(self, point_):
-        """
-        Compute the magnetostatic scalar potential (satisfying \\(\\vec{H} = -\\nabla \\phi \\))
-        
-        Parameters
-        ----------
-        point: (3,) array of float64
-            Position at which to compute the field.
-        
-        Returns
-        -------
-        Potential as a float value (in units of A).
-        """
-        point = np.array(point_)
-        assert point.shape == (3,), "Please supply a three dimensional point"
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-        return backend.potential_radial(point, charges, jacobians, positions)
-    
-    def current_potential_axial(self, z):
-        assert isinstance(z, float)
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-        return backend.current_potential_axial(z, currents, jacobians, positions)
-     
-    def get_electrostatic_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the electrostatic potential a points on the optical axis (z-axis). 
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-        charges = self.electrostatic_point_charges.charges
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return backend.axial_derivatives_radial(z, charges, jacobians, positions)
-    
-    def get_magnetostatic_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the magnetostatic potential at points on the optical axis (z-axis). 
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-        charges = self.magnetostatic_point_charges.charges
-        jacobians = self.magnetostatic_point_charges.jacobians
-        positions = self.magnetostatic_point_charges.positions
-         
-        derivs_magnetic = backend.axial_derivatives_radial(z, charges, jacobians, positions)
-        derivs_current = self.get_current_axial_potential_derivatives(z)
-        return derivs_magnetic + derivs_current
-     
-    def get_current_axial_potential_derivatives(self, z):
-        """
-        Compute the derivatives of the current magnetostatic scalar potential at points on the optical axis.
-         
-        Parameters
-        ----------
-        z : (N,) np.ndarray of float64
-            Positions on the optical axis at which to compute the derivatives.
-         
-        Returns
-        ------- 
-        Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so
-        at position 0 the potential itself is returned). The highest derivative returned is a 
-        constant currently set to 9."""
-
-        currents = self.current_point_charges.charges
-        jacobians = self.current_point_charges.jacobians
-        positions = self.current_point_charges.positions
-        return backend.current_axial_derivatives_radial(z, currents, jacobians, positions)
-      
-    def area_of_element(self, i):
-        jacobians = self.electrostatic_point_charges.jacobians
-        positions = self.electrostatic_point_charges.positions
-        return 2*np.pi*np.sum(jacobians[i] * positions[i, :, 0])
-    
-    def get_tracer(self, bounds):
-        return T.TracerRadialBEM(self, bounds)
-
-

Ancestors

- -

Methods

-
-
-def area_of_element(self, i) -
-
-
-
-
-def current_field_at_point(self, point_) -
-
-
-
-
-def current_potential_axial(self, z) -
-
-
-
-
-def electrostatic_field_at_point(self, point_) -
-
-

Compute the electric field, \vec{E} = -\nabla \phi

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) array of float64, containing the field strengths (units of V/m)

-
-
-def electrostatic_potential_at_point(self, point_) -
-
-

Compute the electrostatic potential.

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of V).

-
-
-def get_current_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the current magnetostatic scalar potential at points on the optical axis.

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_electrostatic_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the electrostatic potential a points on the optical axis (z-axis).

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_magnetostatic_axial_potential_derivatives(self, z) -
-
-

Compute the derivatives of the magnetostatic potential at points on the optical axis (z-axis).

-

Parameters

-
-
z : (N,) np.ndarray of float64
-
Positions on the optical axis at which to compute the derivatives.
-
-

Returns

-

Numpy array of shape (N, 9) containing the derivatives. At index i one finds the i-th derivative (so -at position 0 the potential itself is returned). The highest derivative returned is a -constant currently set to 9.

-
-
-def get_tracer(self, bounds) -
-
-
-
-
-def magnetostatic_field_at_point(self, point_) -
-
-

Compute the magnetic field \vec{H}

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

(3,) np.ndarray of float64 containing the field strength (in units of A/m) in the x, y and z directions.

-
-
-def magnetostatic_potential_at_point(self, point_) -
-
-

Compute the magnetostatic scalar potential (satisfying \vec{H} = -\nabla \phi )

-

Parameters

-
-
point : (3,) array of float64
-
Position at which to compute the field.
-
-

Returns

-

Potential as a float value (in units of A).

-
-
-

Inherited members

- -
-
-
-
- -
- - - diff --git a/docs/docs/v0.7.0rc3/tracing.html b/docs/docs/v0.7.0rc3/tracing.html deleted file mode 100644 index fb20b345..00000000 --- a/docs/docs/v0.7.0rc3/tracing.html +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - -traceon.tracing API documentation - - - - - - - - - - - - - -
-
-
-

Module traceon.tracing

-
-
-

The tracing module allows to trace electrons within any field type returned by the traceon.solver module. The tracing algorithm -used is RK45 with adaptive step size control [1]. The tracing code is implemented in C (see traceon.backend) and has therefore -excellent performance. The module also provides various helper functions to define appropriate initial velocity vectors and to -compute intersections of the computed traces with various planes.

-

References

-

[1] Erwin Fehlberg. Low-Order Classical Runge-Kutta Formulas With Stepsize Control and their Application to Some Heat -Transfer Problems. 1969. National Aeronautics and Space Administration.

-
-
-
-
-
-
-

Functions

-
-
-def axis_intersection(positions) -
-
-

Compute the z-value of the intersection of the trajectory with the x=0 plane. -Note that this function will not work properly if the trajectory crosses the x=0 plane zero or multiple times.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions (and velocities) of an electron as returned by Tracer.
-
-

Returns

-
-
float, z-value of the intersection with the x=0 plane
-
 
-
-
-
-def plane_intersection(positions, p0, normal) -
-
-

Compute the intersection of a trajectory with a general plane in 3D. The plane is specified -by a point (p0) in the plane and a normal vector (normal) to the plane. The intersection -point is calculated using a linear interpolation.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions of an electron as returned by Tracer.
-
p0 : (3,) np.ndarray of float64
-
A point that lies in the plane.
-
normal : (3,) np.ndarray of float64
-
A vector that is normal to the plane. A point p lies in the plane iff dot(normal, p - p0) = 0 where -dot is the dot product.
-
-

Returns

-

np.ndarray of shape (6,) containing the position and velocity of the electron at the intersection point.

-
-
-def velocity_vec(eV, direction_) -
-
-

Compute an initial velocity vector in the correct units and direction.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
direction : (3,) numpy array
-
vector giving the correct direction of the initial velocity vector. Does not -have to be a unit vector as it is always normalized.
-
-

Returns

-

Initial velocity vector with magnitude corresponding to the supplied energy (in eV). -The shape of the resulting vector is the same as the shape of direction.

-
-
-def velocity_vec_spherical(eV, theta, phi) -
-
-

Compute initial velocity vector given energy and direction computed from spherical coordinates.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
theta : float
-
angle with z-axis (same definition as theta in a spherical coordinate system)
-
phi : float
-
angle with the x-axis (same definition as phi in a spherical coordinate system)
-
-

Returns

-

Initial velocity vector of shape (3,) with magnitude corresponding to the supplied energy (in eV).

-
-
-def velocity_vec_xz_plane(eV, angle, downward=True) -
-
-

Compute initial velocity vector in the xz plane with the given energy and angle with z-axis.

-

Parameters

-
-
eV : float
-
initial energy in units of eV
-
angle : float
-
angle with z-axis
-
downward : bool
-
whether the velocity vector should point upward or downwards
-
-

Returns

-

Initial velocity vector of shape (3,) with magnitude corresponding to the supplied energy (in eV).

-
-
-def xy_plane_intersection(positions, z) -
-
-

Compute the intersection of a trajectory with an xy-plane.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions (and velocities) of an electron as returned by Tracer.
-
z : float
-
z-coordinate of the plane with which to compute the intersection
-
-

Returns

-

(6,) array of float64, containing the position and velocity of the electron at the intersection point.

-
-
-def xz_plane_intersection(positions, y) -
-
-

Compute the intersection of a trajectory with an xz-plane.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions (and velocities) of an electron as returned by Tracer.
-
z : float
-
z-coordinate of the plane with which to compute the intersection
-
-

Returns

-

(6,) array of float64, containing the position and velocity of the electron at the intersection point.

-
-
-def yz_plane_intersection(positions, x) -
-
-

Compute the intersection of a trajectory with an yz-plane.

-

Parameters

-
-
positions : (N, 6) np.ndarray of float64
-
Positions (and velocities) of an electron as returned by Tracer.
-
z : float
-
z-coordinate of the plane with which to compute the intersection
-
-

Returns

-

(6,) array of float64, containing the position and velocity of the electron at the intersection point.

-
-
-
-
-

Classes

-
-
-class Tracer -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer:
-    """General electron tracer class. Can trace electrons given any field class from `traceon.solver`.
-
-    Parameters
-    ----------
-    field: traceon.solver.Field (or any class inheriting Field)
-        The field used to compute the force felt by the electron.
-    bounds: (3, 2) np.ndarray of float64
-        Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-    """
-    
-    def __init__(self, field, bounds):
-        self.field = field
-         
-        bounds = np.array(bounds).astype(np.float64)
-        assert bounds.shape == (3,2)
-        self.bounds = bounds
-    
-    def __str__(self):
-        field_name = self.field.__class__.__name__
-        bounds_str = ' '.join([f'({bmin:.2f}, {bmax:.2f})' for bmin, bmax in self.bounds])
-        return f'<Traceon Tracer of {field_name},\n\t' \
-            + 'Bounds: ' + bounds_str + ' mm >'
-    
-    def __call__(self, position, velocity):
-        """Trace an electron.
-
-        Parameters
-        ----------
-        position: (3,) np.ndarray of float64
-            Initial position of electron.
-        velocity: (3,) np.ndarray of float64
-            Initial velocity (expressed in a vector whose magnitude has units of eV). Use one of the utility functions documented
-            above to create the initial velocity vector.
-        atol: float
-            Absolute tolerance determining the accuracy of the trace.
-        
-        Returns
-        -------
-        `(times, positions)` which is a tuple of two numpy arrays. `times` is one dimensional and contains the times
-        (in ns) at which the positions have been computed. The `positions` array is two dimensional, `positions[i]` correspond
-        to time step `times[i]`. One element of the positions array has shape (6,).
-        The first three elements in the `positions[i]` array contain the x,y,z positions.
-        The last three elements in `positions[i]` contain the vx,vy,vz velocities.
-        """
-        raise RuntimeError('Please use the field.get_tracer(...) method to get the appropriate Tracer instance')
-
-

Subclasses

- -

Methods

-
-
-def __call__(self, position, velocity) -
-
-

Trace an electron.

-

Parameters

-
-
position : (3,) np.ndarray of float64
-
Initial position of electron.
-
velocity : (3,) np.ndarray of float64
-
Initial velocity (expressed in a vector whose magnitude has units of eV). Use one of the utility functions documented -above to create the initial velocity vector.
-
atol : float
-
Absolute tolerance determining the accuracy of the trace.
-
-

Returns

-

(times, positions) which is a tuple of two numpy arrays. times is one dimensional and contains the times -(in ns) at which the positions have been computed. The positions array is two dimensional, positions[i] correspond -to time step times[i]. One element of the positions array has shape (6,). -The first three elements in the positions[i] array contain the x,y,z positions. -The last three elements in positions[i] contain the vx,vy,vz velocities.

-
-
-
-
-class Tracer3DAxial -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer3DAxial(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        velocity = _convert_velocity_to_SI(velocity)
-        return backend.trace_particle_3d_derivs(position, velocity, self.bounds, atol,
-            self.field.z, self.field.electrostatic_coeffs, self.field.magnetostatic_coeffs)
-
-

Ancestors

- -

Inherited members

- -
-
-class Tracer3D_BEM -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class Tracer3D_BEM(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        velocity = _convert_velocity_to_SI(velocity)
-        bounds = self.field.field_bounds
-        elec, mag = self.field.electrostatic_point_charges, self.field.magnetostatic_point_charges
-        return backend.trace_particle_3d(position, velocity, self.bounds, atol, elec, mag)
-
-

Ancestors

- -

Inherited members

- -
-
-class TracerRadialAxial -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class TracerRadialAxial(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-
-        velocity = _convert_velocity_to_SI(velocity)
-        
-        elec, mag = self.field.electrostatic_coeffs, self.field.magnetostatic_coeffs
-        
-        return backend.trace_particle_radial_derivs(position, velocity, self.bounds, atol, self.field.z, elec, mag)
-
-

Ancestors

- -

Inherited members

- -
-
-class TracerRadialBEM -(field, bounds) -
-
-

General electron tracer class. Can trace electrons given any field class from traceon.solver.

-

Parameters

-
-
field : Field (or any class inheriting Field)
-
The field used to compute the force felt by the electron.
-
bounds : (3, 2) np.ndarray of float64
-
Once the electron reaches one of the boundaries the tracing stops. The bounds are of the form ( (xmin, xmax), (ymin, ymax), (zmin, zmax) ).
-
-
- -Expand source code - -
class TracerRadialBEM(Tracer):
-    def __call__(self, position, velocity, atol=1e-10):
-        
-        velocity = _convert_velocity_to_SI(velocity)
-        
-        return backend.trace_particle_radial(
-                position,
-                velocity, 
-                self.bounds,
-                atol, 
-                self.field.electrostatic_point_charges,
-                self.field.magnetostatic_point_charges,
-                self.field.current_point_charges,
-                field_bounds=self.field.field_bounds)
-
-

Ancestors

- -

Inherited members

- -
-
-
-
- -
- - -