bluemira.equilibria.coils._grouping =================================== .. py:module:: bluemira.equilibria.coils._grouping .. autoapi-nested-parse:: Coil and coil grouping objects Classes ------- .. autoapisummary:: bluemira.equilibria.coils._grouping.CoilGroup bluemira.equilibria.coils._grouping.Circuit bluemira.equilibria.coils._grouping.SymmetricCircuit bluemira.equilibria.coils._grouping.CoilSetOptimisationState bluemira.equilibria.coils._grouping.CoilSet Functions --------- .. autoapisummary:: bluemira.equilibria.coils._grouping.symmetrise_coilset Module Contents --------------- .. py:function:: symmetrise_coilset(coilset: CoilSet, *, rtol: float | None = None, symmetrise_singular: bool = False) -> CoilSet Symmetrise a CoilSet by converting any coils that are up-down symmetric about z=0 to SymmetricCircuits. :param coilset: CoilSet to symmetrise :param rtol: Relative tolerance used when comparing coil values. The values for the secondary coil in the pair will be set to be equal to the primary coil values if they are within rtol. :param symmetrise_singular: make singular coils symmetric about z=0 :returns: New CoilSet with SymmetricCircuits where appropriate :raises EquilibriaError: Superposition of coils or unrecognised type .. py:class:: CoilGroup(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup, psi_analytic: bool = False, Bx_analytic: bool = True, Bz_analytic: bool = True) Bases: :py:obj:`bluemira.equilibria.coils._field.CoilGroupFieldsMixin` .. autoapi-inheritance-diagram:: bluemira.equilibria.coils._grouping.CoilGroup :parts: 1 :private-bases: Coil Grouping object Allow nested coils or groups of coils with access to the methods and properties in the same way to a vanilla Coil :param coils: Coils and groups of Coils to group .. py:attribute:: __slots__ :value: ('_Bx_analytic', '_Bz_analytic', '_coils', '_pad_size', '_psi_analytic') .. py:attribute:: _coils :value: () .. py:method:: __repr__() Pretty print .. py:method:: n_coils(ctype: str | bluemira.base.constants.CoilType | None = None) -> int Get number of coils :param ctype: get number of coils of a specific type :returns: Number of coils .. py:method:: plot(ax: matplotlib.axes.Axes | None = None, *, subcoil: bool = True, label: bool = False, force: collections.abc.Iterable | None = None, **kwargs) -> bluemira.equilibria.plotting.CoilGroupPlotter | None Plot a CoilGroup :param ax: Matplotlib axis object :param subcoil: plot coil discretisations :param label: show coil labels on plot :param force: force arrows iterable :param kwargs: passed to matplotlib's Axes.plot .. py:method:: fix_sizes() Fix the sizes of coils in CoilGroup .. py:method:: resize(currents: float | list | numpy.ndarray) Resize coils based on their current if their size is not fixed .. py:method:: _resize(currents: float | list | numpy.ndarray) Resize coils based on their current .. rubric:: Notes Ignores any protections on their size .. py:method:: __run_func(func: str, *args, **kwargs) Runs a function with no outputs that exists on a coil or coilgroup This function aims to deal with the edge cases that are around nested coilgroups If kwargs are passed they will be passed to all function calls as is. If args are passed an attempt is made to push the right shaped argument to a given function. :raises ValueError: Number of arguments not equal to number og coil groups .. py:method:: add_coil(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup) Add coils to the coil group .. py:method:: remove_coil(*coil_name: str, _top_level: bool = True) -> list[str] | None Remove coil from CoilGroup :param coil_name: coil(s) to remove :param _top_level: FOR INTERNAL USE, flags if at top level of nested coilgroup stack :returns: Removed names if not at top level of nested stack :raises EquilibriaError: Unknown coils .. rubric:: Notes If a nested coilgroup is empty it is also removed from the parent coilgroup .. py:method:: from_group_vecs(eqdsk: eqdsk.EQDSKInterface) -> CoilGroup :classmethod: Initialises an instance of CoilSet from group vectors. This has been implemented as a dict operation, because it will occur for eqdsks only. Future dict instantiation methods will likely differ, hence the confusing name of this method. There should always be a coilset but... if for some reason the coilset is missing, and the user has not provided 'user_coils' as an input for 'from_eqdsk', then a dummy coilset is used and a warning message is printed. .. py:method:: to_group_vecs() -> tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray, numpy.ndarray] Output CoilGroup properties as numpy arrays :returns: * *x* -- The x-positions of coils * *z* -- The z-positions of coils. * *dx* -- The coil size in the x-direction. * *dz* -- The coil size in the z-direction. * *currents* -- The coil currents. .. py:method:: __list_getter(attr: str) -> list Get attributes from coils tuple .. py:method:: __getter(attr: str) -> numpy.ndarray :returns: Attribute from coils and convert to flattened numpy array .. py:method:: __quad_getter(attr: str) -> numpy.ndarray :returns: Quadratures and autopad to create non ragged array .. py:method:: __setter(attr: str, values: bluemira.base.constants.CoilType | float | collections.abc.Iterable[bluemira.base.constants.CoilType | float], dtype: type | None = None) Set attributes on coils :raises ValueError: Number of elements < number of coils .. py:method:: __getitem__(item) Get coils .. py:method:: __copy__() Copy dunder method, needed because attribute setter fails for quadratures .. py:method:: __deepcopy__(memo) Deepcopy dunder method, needed because attribute setter fails for quadratures .. py:method:: _pad_discretisation(_to_pad: list[numpy.ndarray]) Convert quadrature list of array to rectangular arrays. Padding quadrature arrays with zeros to allow array operations on rectangular matricies. :param _to_pad: x quadratures .. rubric:: Notes Padding exists for coils with different discretisations or sizes within a coilgroup. There are a few extra calculations of the greens functions where padding exists in :meth:`~bluemira.equilibria.coils._field.CoilGroupFieldsMixin._combined_control`. .. py:method:: _find_coil(name) Find coil by name :raises KeyError: Coil not found in group .. py:method:: _get_coiltype(*ctype: bluemira.base.constants.CoilType | str) -> list[bluemira.equilibria.coils._coil.Coil] Find coil by type .. py:method:: all_coils() -> list[bluemira.equilibria.coils._coil.Coil] :returns: All coils as a flattened list (no CoilGroups) .. py:method:: _get_type_index(*ctype: bluemira.base.constants.CoilType | str) -> numpy.typing.NDArray[int] .. py:method:: get_coiltype(*ctype: str | bluemira.base.constants.CoilType) -> CoilGroup | None :returns: Coils matching coil type .. py:method:: assign_material(ctype, j_max, b_max) Assign material J and B to Coilgroup .. py:method:: get_max_current(max_current: float = np.inf) -> numpy.ndarray Get max currents If a max current argument is provided and the max current isn't set, the value will be as input. :param max_current: max current value if j_max == nan :returns: Maximum currents .. py:property:: name :type: list Get coil names .. py:property:: primary_coil :type: bluemira.equilibria.coils._coil.Coil Get primary coil, which is arbitrarily taken to be the first coil in the group. Will recurse if the first coil itself is a CoilGroup. .. py:method:: get_coil_or_group_with_coil_name(coil_name: str) -> bluemira.equilibria.coils._coil.Coil | CoilGroup Get the coil or coil group with the coil with `coil_name` in it. This will be the lowest level group that contains the coil. :param coil_name: The coil name to search for :returns: The coil or coil group with the given coil name in it :raises ValueError: No coil found with given name .. py:property:: x :type: numpy.ndarray Get coil x positions .. py:property:: z :type: numpy.ndarray Get coil z positions .. py:property:: position Get coil x, z positions .. py:property:: ctype :type: list Get coil types .. py:property:: dx :type: numpy.ndarray Get coil widths (half) .. py:property:: dz :type: numpy.ndarray Get coil heights (half) .. py:property:: current :type: numpy.ndarray Get coil currents .. py:property:: j_max :type: numpy.ndarray Get coil max current density .. py:property:: b_max :type: numpy.ndarray Get coil max field .. py:property:: resistance :type: numpy.ndarray Get coil resistance .. py:property:: discretisation :type: numpy.ndarray Get coil discretisations .. py:property:: n_turns :type: numpy.ndarray Get coil number of turns .. py:property:: area :type: numpy.ndarray Get coil areas .. py:property:: volume :type: numpy.ndarray Get coil volumes .. py:property:: x_boundary :type: numpy.ndarray Get coil x coordinate boundary .. py:property:: z_boundary :type: numpy.ndarray Get coil z coordinate boundary .. py:property:: _flag_sizefix :type: numpy.ndarray Get coil current radius .. py:property:: _current_radius :type: numpy.ndarray Get coil current radius .. py:property:: _quad_x Get coil x quadratures .. py:property:: _quad_z Get coil z quadratures .. py:property:: _quad_dx Get coil dx quadratures .. py:property:: _quad_dz Get coil dz quadratures .. py:property:: _quad_weighting Get coil quadrature weightings .. py:property:: _quad_boundary Get coil quadrature boundaries .. py:class:: Circuit(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup, current: float | None = None, psi_analytic: bool = False, Bx_analytic: bool = True, Bz_analytic: bool = True) Bases: :py:obj:`CoilGroup` .. autoapi-inheritance-diagram:: bluemira.equilibria.coils._grouping.Circuit :parts: 1 :private-bases: A CoilGroup where all coils have the same current :param coils: coils in circuit :param current: The current value, if not provided the first coil current is used .. py:attribute:: __slots__ :value: ('_Bx_analytic', '_Bz_analytic', '_coils', '_pad_size', '_psi_analytic') .. py:attribute:: current Get coil currents .. py:method:: _get_current() .. py:property:: primary_group :type: bluemira.equilibria.coils._coil.Coil | CoilGroup Get the first coil or group in the circuit as the 'primary_group' .. py:method:: add_coil(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup) Add coil to circuit forcing the same current .. py:class:: SymmetricCircuit(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup, psi_analytic: bool = False, Bx_analytic: bool = True, Bz_analytic: bool = True) Bases: :py:obj:`Circuit` .. autoapi-inheritance-diagram:: bluemira.equilibria.coils._grouping.SymmetricCircuit :parts: 1 :private-bases: A Circuit with positional symmetry :param coils: 2 coil or coil group objects to symmetrise in a circuit .. rubric:: Notes Although two groups can be defined any movement of the coils is achieved by offsets to the mean position of the coilgroups. Currently only symmetric about z = 0 see gh issue #210 .. py:attribute:: __slots__ :value: ('_symmetry_line', '_shift', 'sym_mat', '_Bx_analytic', '_Bz_analytic', '_coils', '_pad_size',... .. py:property:: symmetric_group :type: bluemira.equilibria.coils._coil.Coil | CoilGroup Get the second coil or group as the 'symmetric_group' .. py:method:: modify_symmetry(symmetry_line: numpy.ndarray) Create a unit vector for the symmetry of the circuit :param symmetry_line: two points making a symmetry line [[float, float], [float, float]] .. py:method:: _symmetry_matrix() Symmetry matrix .. math:: \frac{1}{1 + m} \left[ {\begin{array}{cc} 1 - m^2 & 2m \\ 2m & -(1 - m^2) \\ \end{array} } \right] .. py:method:: _symmetrise() -> numpy.ndarray Calculate the change in position to the symmetric coil, twice the distance to the line of symmetry. :returns: The change in position .. py:method:: remove_coil(*coil_name: str, _top_level: bool = True) -> list[str] | None Remove coil(s) from CoilSet :param coil_name: coil(s) to remove :param _top_level: FOR INTERNAL USE, flags if at top level of nested coilgroup stack :returns: Removed coil names, if not at top level of nested stack :raises ValueError: Cannot remove coils from a SymmetricCircuit with nested CoilGroups .. py:method:: _get_primary_group_x_centre() -> numpy.float64 Get the x centre of the first coil group .. py:method:: _get_primary_group_z_centre() -> numpy.float64 Get the z centre of the first coil group .. py:method:: _get_symmetric_group_x_centre() -> numpy.float64 Get the x centre of the first coil group .. py:method:: _get_symmetric_group_z_centre() -> numpy.float64 Get the z centre of the first coil group .. py:method:: _get_group_centre() -> numpy.ndarray Get the centre of the first coil group .. py:class:: CoilSetOptimisationState State of the optimisation of a CoilSet :param currents: The current values of the coils :param xs: The x positions of the coils :param zs: The z positions of the coils .. py:attribute:: currents :type: numpy.ndarray .. py:attribute:: xs :type: numpy.ndarray .. py:attribute:: zs :type: numpy.ndarray .. py:method:: __post_init__() .. py:property:: positions :type: numpy.ndarray Get the positions as a (2, N) array .. py:property:: positions_flat :type: numpy.ndarray Get the positions as an array with xs and zs concatenated (xs then zs) .. py:class:: CoilSet(*coils: bluemira.equilibria.coils._coil.Coil | CoilGroup, control_names: list | bool | None = None, psi_analytic: bool = False, Bx_analytic: bool = True, Bz_analytic: bool = True) Bases: :py:obj:`bluemira.equilibria.coils._field.CoilSetFieldsMixin`, :py:obj:`CoilGroup` .. autoapi-inheritance-diagram:: bluemira.equilibria.coils._grouping.CoilSet :parts: 1 :private-bases: CoilSet is a CoilGroup with the concept of control coils A CoilSet will return the total volume and area of the coils and the respective psi or field calculations can optionally sum over the coils or only sum over the control coils. By default all coils are controlled :param coils: The coils to be added to the set :param control_names: List of coil names to be controlled .. py:attribute:: __slots__ :value: ('_control', '_control_ind', '_Bx_analytic', '_Bz_analytic', '_coils', '_pad_size', '_psi_analytic') .. py:property:: control :type: list[str] Get control coil names .. py:method:: remove_coil(*coil_name: str, _top_level: bool = True) -> list[str] | None Remove coil(s) from CoilSet :param coil_name: coil(s) to remove :param _top_level: FOR INTERNAL USE, flags if at top level of nested coilgroup stack :returns: Removed coil names, if not at top level of nested stack .. py:property:: n_control :type: float Number of coils being actively controlled .. py:method:: get_control_coils() -> CoilSet Get control coils .. py:method:: get_uncontrolled_coils() -> CoilSet :returns: Uncontrolled coils .. py:property:: area :type: float Cross sectional area of CoilSet .. py:property:: volume :type: float Volume of Coilset .. py:method:: _sum(output: numpy.ndarray, *, sum_coils: bool = False, control: bool = False) -> numpy.ndarray Get responses of coils optionally only control and/or sum over the responses :param output: Output of calculation :param sum_coils: sum over coils :param control: operations on control coils only :returns: Summed response output .. py:method:: get_coiltype(*ctype: str | bluemira.base.constants.CoilType) -> CoilSet | None Get coils by coils type .. py:method:: from_group_vecs(eqdsk: eqdsk.EQDSKInterface, control_coiltypes=(CoilType.PF, CoilType.CS)) -> CoilGroup :classmethod: Create CoilSet from eqdsk group vectors. Automatically sets all coils that are not implicitly passive to control coils .. py:method:: get_optimisation_state(position_coil_names: list[str] | None = None, current_scale: float = 1.0) -> CoilSetOptimisationState Get the state of the CoilSet for optimisation. If control coils are set then this is accounted for. :param position_coil_names: The names of the coils to get the positions of :param current_scale: The scale of the currents :returns: The state of the CoilSet for optimisation :rtype: CoilSetOptimisationState .. py:method:: set_optimisation_state(opt_currents: numpy.ndarray | None = None, coil_position_map: dict[str, numpy.ndarray] | None = None, current_scale: float = 1.0) Set the state of the CoilSet, post optimisation. If control coils are set then this is accounted for. Used in conjunction with `get_optimisation_state`. :param opt_currents: The optimisation currents :param coil_position_map: The map of coil names to positions: [x, z] :param current_scale: The scale of the currents .. py:property:: n_current_optimisable_coils :type: int Get the number of all current optimisable coils. If control coils are set then this is accounted for. This will only count the primary coil in the case of a SymmetricCircuit pair or coils. .. py:property:: current_optimisable_coil_names :type: list[str] Get the names of all current optimisable coils. If control coils are set then this is accounted for. This will be the primary coil in the case of a SymmetricCircuit pair or coils. .. py:property:: all_current_optimisable_coils :type: list[bluemira.equilibria.coils._coil.Coil] Get a list of all coils that can be current optimised. If control coils are set then this is accounted for. .. py:method:: get_current_optimisable_coils(coil_names: list[str] | None = None) -> CoilSet Get a coils that can be current optimised. If control coils are set then this is accounted for. If coil_names is given, only the coils with those names are returned. Names in coil_names must be a subset of current optimisable coils. :param coil_names: The names of the coils to get. If None, all current optimisable coils are returned. :raises ValueError: If a name in `coil_names` in not in `all_current_optimisable_coils`. .. py:property:: _opt_currents_inds :type: list[int] Get the indices of the coils that can be optimised. If control coils are set then this is accounted for. These indices are used to extract the optimisable currents from the CoilSet and are based on the index of the coils in the name array. .. py:property:: _contains_circuits :type: bool A simple check to see if the CoilSet contains any Circuits. .. py:property:: _opt_currents_expand_mat :type: numpy.ndarray Get the optimisation currents expansion matrix. This matrix is used to convert the optimisable currents to the full set of currents in the CoilSet. :raises ValueError: Number of optimisable coils not equal number of groups/coils .. py:property:: _opt_currents :type: numpy.ndarray Get the currents for the optimisable coils. If control coils are set then this is accounted for. .. py:property:: n_position_optimisable_coils :type: int Get the number of coils that can be position optimised. If control coils are set then this is accounted for. This will only count the primary coil in the case of a SymmetricCircuit pair or coils. .. py:property:: position_optimisable_coil_names :type: list[str] Get the names of the coils that can be position optimised. If control coils are set then this is accounted for. This will be the primary coil in the case of a SymmetricCircuit pair or coils. .. py:property:: all_position_optimisable_coils :type: list[bluemira.equilibria.coils._coil.Coil] Get the names of all coils that can be position optimised. If control coils are set then this is accounted for. .. py:method:: get_position_optimisable_coils(coil_names: list[str] | None = None) -> CoilSet Get the coils that can be position optimised. If control coils are set then this is accounted for. :param coil_names: The names of the coils to get the positions of. If None, all position optimisable coils are returned. :raises ValueError: Coil's position not optimisable .. py:method:: _get_opt_positions(position_coil_names: list[str] | None = None) -> tuple[numpy.ndarray, numpy.ndarray] Get the positions of the position optimisable coils. :param position_coil_names: The names of the coils to get the positions of. If None, all position optimisable coils are returned. :returns: * The x coordinates * The z coordinates .. py:method:: _set_opt_positions(coil_position_map: dict[str, numpy.ndarray]) -> None Set the positions of the position optimisable coils.