bluemira.equilibria.flux_surfaces ================================= .. py:module:: bluemira.equilibria.flux_surfaces .. autoapi-nested-parse:: Flux surface utility classes and calculations Classes ------- .. autoapisummary:: bluemira.equilibria.flux_surfaces.FluxSurface bluemira.equilibria.flux_surfaces.ClosedFluxSurface bluemira.equilibria.flux_surfaces.OpenFluxSurface bluemira.equilibria.flux_surfaces.PartialOpenFluxSurface bluemira.equilibria.flux_surfaces.CoreResults bluemira.equilibria.flux_surfaces.FieldLine bluemira.equilibria.flux_surfaces.FieldLineTracer Functions --------- .. autoapisummary:: bluemira.equilibria.flux_surfaces._flux_surface_dl bluemira.equilibria.flux_surfaces.analyse_plasma_core bluemira.equilibria.flux_surfaces.calculate_connection_length_flt bluemira.equilibria.flux_surfaces.calculate_connection_length_fs bluemira.equilibria.flux_surfaces.poloidal_angle Module Contents --------------- .. py:function:: _flux_surface_dl(x, dx, dz, Bp, Bt) .. py:class:: FluxSurface(geometry: bluemira.geometry.coordinates.Coordinates) Flux surface base class. :param geometry: Flux surface geometry object .. py:attribute:: __slots__ :value: ('coords',) .. py:attribute:: coords .. py:property:: x_start :type: float Start radial coordinate of the FluxSurface. .. py:property:: z_start :type: float Start vertical coordinate of the FluxSurface. .. py:property:: x_end :type: float End radial coordinate of the FluxSurface. .. py:property:: z_end :type: float End vertical coordinate of the FluxSurface. .. py:method:: _dl(eq) .. py:method:: connection_length(eq: bluemira.equilibria.equilibrium.Equilibrium) -> float Calculate the parallel connection length along a field line (i.e. flux surface). :param eq: Equilibrium from which the FluxSurface was extracted :returns: Connection length from the start of the flux surface to the end of the flux surface .. py:method:: plot(ax: matplotlib.pyplot.Axes | None = None, **kwargs) Plot the FluxSurface. .. py:method:: copy() Make a deep copy of the FluxSurface. :returns: An instance of FluxSurface, with exactly the same class and coordinates, but sharing a different underlying copy of the data (Coordinates). .. py:class:: ClosedFluxSurface(geometry: bluemira.geometry.coordinates.Coordinates) Bases: :py:obj:`FluxSurface` .. autoapi-inheritance-diagram:: bluemira.equilibria.flux_surfaces.ClosedFluxSurface :parts: 1 :private-bases: Utility class for closed flux surfaces. .. py:attribute:: __slots__ :value: ('_p1', '_p2', '_p3', '_p4', '_z_centre') .. py:attribute:: _p1 .. py:attribute:: _p2 .. py:attribute:: _p3 .. py:attribute:: _p4 .. py:attribute:: _z_centre .. py:property:: major_radius :type: float Major radius of the ClosedFluxSurface. .. py:property:: minor_radius :type: float Minor radius of the ClosedFluxSurface. .. py:property:: aspect_ratio :type: float Aspect ratio of the ClosedFluxSurface. .. py:property:: kappa :type: float Average elongation of the ClosedFluxSurface. .. py:property:: kappa_upper :type: float Upper elongation of the ClosedFluxSurface. .. py:property:: kappa_lower :type: float Lower elongation of the ClosedFluxSurface. .. py:property:: delta :type: float Average triangularity of the ClosedFluxSurface. .. py:property:: delta_upper :type: float Upper triangularity of the ClosedFluxSurface. .. py:property:: delta_lower :type: float Lower triangularity of the ClosedFluxSurface. .. py:property:: zeta :type: float Average squareness of the ClosedFluxSurface. .. py:property:: zeta_upper :type: float Outer upper squareness of the ClosedFluxSurface. .. py:property:: zeta_lower :type: float Outer lower squareness of the ClosedFluxSurface. .. py:method:: _zeta_calc(a, b, xa, za, xd, zd) Actual squareness calculation .. rubric:: Notes Squareness defined here w.r.t an ellipse intersection along a projected line .. py:property:: area :type: float Enclosed area of the ClosedFluxSurface. .. py:property:: volume :type: float Volume of the ClosedFluxSurface. .. py:method:: shafranov_shift(eq: bluemira.equilibria.equilibrium.Equilibrium) -> tuple[float, float] Calculate the Shafranov shift of the ClosedFluxSurface. :param eq: Equilibrium with which to calculate the safety factor :returns: * *dx_shaf* -- Radial Shafranov shift * *dz_shaf* -- Vertical Shafranov shift .. py:method:: safety_factor(eq: bluemira.equilibria.equilibrium.Equilibrium) -> float Calculate the cylindrical safety factor of the ClosedFluxSurface. The ratio of toroidal turns to a single full poloidal turn. :param eq: Equilibrium with which to calculate the safety factor :rtype: Cylindrical safety factor of the closed flux surface .. py:class:: OpenFluxSurface(coords: bluemira.geometry.coordinates.Coordinates) Bases: :py:obj:`FluxSurface` .. autoapi-inheritance-diagram:: bluemira.equilibria.flux_surfaces.OpenFluxSurface :parts: 1 :private-bases: Utility class for handling open flux surface geometries. .. py:attribute:: __slots__ :value: () .. py:method:: split(psi_point: bluemira.equilibria.find.PsiPoint, plane: bluemira.geometry.plane.BluemiraPlane | None = None) -> tuple[PartialOpenFluxSurface, PartialOpenFluxSurface] Split an OpenFluxSurface into two separate PartialOpenFluxSurfaces about a horizontal plane. N.B. The magnetic centre of the plasma (O-point) is often the value that is used for the PsiPoint and X-Y plane. :param psi_point: Point at which to make the split. :param plane: The x-y cutting plane. Will default to the psi_point x-y plane :returns: * *down* -- The downwards open flux surfaces from the splitting point * *up* -- The upwards open flux surfaces from the splitting point .. py:class:: PartialOpenFluxSurface(coords: bluemira.geometry.coordinates.Coordinates) Bases: :py:obj:`OpenFluxSurface` .. autoapi-inheritance-diagram:: bluemira.equilibria.flux_surfaces.PartialOpenFluxSurface :parts: 1 :private-bases: Utility class for a partial open flux surface, i.e. an open flux surface that has been split at the midplane and only has one intersection with the wall. .. py:attribute:: __slots__ :value: ('alpha',) .. py:attribute:: alpha :value: None .. py:method:: clip(first_wall: bluemira.geometry.coordinates.Coordinates) Clip the PartialOpenFluxSurface to a first wall. :param first_wall: The geometry of the first wall to clip the OpenFluxSurface to .. py:method:: flux_expansion(eq: bluemira.equilibria.equilibrium.Equilibrium) -> float Flux expansion of the PartialOpenFluxSurface. :param eq: Equilibrium with which to calculate the flux expansion :rtype: Target flux expansion .. py:class:: CoreResults Dataclass for core results. .. py:attribute:: psi_n :type: collections.abc.Iterable[float] .. py:attribute:: R_0 :type: collections.abc.Iterable[float] .. py:attribute:: a :type: collections.abc.Iterable[float] .. py:attribute:: A :type: collections.abc.Iterable[float] .. py:attribute:: area :type: collections.abc.Iterable[float] .. py:attribute:: V :type: collections.abc.Iterable[float] .. py:attribute:: kappa :type: collections.abc.Iterable[float] .. py:attribute:: delta :type: collections.abc.Iterable[float] .. py:attribute:: zeta :type: collections.abc.Iterable[float] .. py:attribute:: kappa_upper :type: collections.abc.Iterable[float] .. py:attribute:: delta_upper :type: collections.abc.Iterable[float] .. py:attribute:: zeta_upper :type: collections.abc.Iterable[float] .. py:attribute:: kappa_lower :type: collections.abc.Iterable[float] .. py:attribute:: delta_lower :type: collections.abc.Iterable[float] .. py:attribute:: zeta_lower :type: collections.abc.Iterable[float] .. py:attribute:: q :type: collections.abc.Iterable[float] .. py:attribute:: Delta_shaf :type: collections.abc.Iterable[float] .. py:method:: __post_init__() Setup plot units .. py:method:: to_dict(*, latex_unit: bool = False) Add appropriate units to value names. Make latex ready if latex=true. :returns: Dictionary with updated keys for tables and plotting. .. py:function:: analyse_plasma_core(eq: bluemira.equilibria.equilibrium.Equilibrium, n_points: int = 50) -> CoreResults Analyse plasma core parameters across the normalised 1-D flux coordinate. :rtype: Results dataclass .. py:class:: FieldLine(coords: bluemira.geometry.coordinates.Coordinates, connection_length: float) Field line object. :param coords: Geometry of the FieldLine :param connection_length: Connection length of the FieldLine .. py:attribute:: coords .. py:attribute:: connection_length .. py:method:: plot(ax: matplotlib.pyplot.Axes | None = None, **kwargs) Plot the FieldLine. :param ax: Matplotlib axes onto which to plot .. py:method:: pointcare_plot(ax: matplotlib.pyplot.Axes | None = None) Poincaré plot of the field line intersections with the half-xz-plane. :param ax: Matplotlib axes onto which to plot .. py:class:: FieldLineTracer(eq: bluemira.equilibria.equilibrium.Equilibrium, first_wall: bluemira.equilibria.grid.Grid | bluemira.geometry.coordinates.Coordinates | None = None) Field line tracing tool. :param eq: Equilibrium in which to trace a field line :param first_wall: Boundary at which to stop tracing the field line .. rubric:: Notes Totally pinched some maths from Ben Dudson's FreeGS here... Perhaps one day I can return the favours. I needed it to compare the analytical connection length calculation with something, so I nicked this but changed the way the equation is solved. Note that this will properly trace field lines through Coils as it doesn't rely on the psi map (which is inaccurate inside Coils). .. py:class:: CollisionTerminator(boundary: bluemira.equilibria.grid.Grid | bluemira.geometry.coordinates.Coordinates) Private Event handling object for solve_ivp :param boundary: Boundary at which to stop tracing the field line. :type boundary: Union[Grid, Coordinates] .. py:attribute:: boundary .. py:attribute:: terminal :value: True .. py:method:: __call__(_phi, xz, *_args) Function handle for the CollisionTerminator :returns: The distance to the given xz point .. py:method:: _call_grid(xz) Function handle for the CollisionTerminator in the case of a Grid. (slight speed improvement) :returns: the minimal distance to the grid .. py:method:: _call_coordinates(xz) Function handle for the CollisionTerminator in the case of Coordinates. :returns: the minimal distance to the grid .. py:attribute:: eq .. py:attribute:: first_wall :value: None .. py:method:: trace_field_line(x: float, z: float, n_points: int = 200, *, forward: bool = True, n_turns_max: int = 20) -> FieldLine Trace a single field line starting at a point. :param x: Radial coordinate of the starting point :param z: Vertical coordinate of the starting point :param n_points: Number of points along the field line :param forward: Whether or not to step forward or backward (+B or -B) :param n_turns_max: Maximum number of toroidal turns to trace the field line :type n_turns_max: Union[int, float] :returns: Resulting field line .. py:method:: _dxzl_dphi(_phi, xz, forward) Credit: Dr. B. Dudson, FreeGS. :returns: The dx, dz, and dl .. py:method:: _process_result(result) :staticmethod: .. py:function:: calculate_connection_length_flt(eq: bluemira.equilibria.equilibrium.Equilibrium, x: float, z: float, first_wall: bluemira.geometry.coordinates.Coordinates | bluemira.equilibria.grid.Grid, *, forward: bool = True, n_points: int = 200, n_turns_max: int = 50) -> float Calculate the parallel connection length from a starting point to a flux-intercepting surface using a field line tracer. :param eq: Equilibrium in which to calculate the connection length :param x: Radial coordinate of the starting point :param z: Vertical coordinate of the starting point :param forward: Whether or not to follow the field line forwards or backwards (+B or -B) :param first_wall: Flux-intercepting surface. Defaults to the grid of the equilibrium :param n_turns_max: Maximum number of toroidal turns to trace the field line :returns: * *Parallel connection length along the field line from the starting point to the* * *intersection point [m]* .. rubric:: Notes More mathematically accurate, but needs additional configuration. Will not likely return a very accurate flux interception point. Also works for closed flux surfaces, but can't tell the difference. Not sensitive to equilibrium grid discretisation. Will work correctly for flux surfaces passing through Coils, but really they should be intercepted beforehand! .. py:function:: calculate_connection_length_fs(eq: bluemira.equilibria.equilibrium.Equilibrium, x: float, z: float, first_wall: bluemira.geometry.coordinates.Coordinates, *, forward: bool = True, f_s: bluemira.geometry.coordinates.Coordinates | None = None) -> float Calculate the parallel connection length from a starting point to a flux-intercepting surface using flux surface geometry. :param eq: Equilibrium in which to calculate the connection length :param x: Radial coordinate of the starting point :param z: Vertical coordinate of the starting point :param forward: Whether or not to follow the field line forwards or backwards :param first_wall: Flux-intercepting surface. Defaults to the grid of the equilibrium :param f_s: Coordinates of flux surface through x and z. :returns: * *Parallel connection length along the field line from the starting point to the* * *intersection point [m]* :raises FluxSurfaceError: If the flux surface at the point in the equilibrium is not an open flux surface .. rubric:: Notes Less mathematically accurate. Will return exact intersection point. Sensitive to equilibrium grid discretisation. Presently does not correctly work for flux surfaces passing through Coils, but really they should be intercepted beforehand! .. py:function:: poloidal_angle(Bp_strike: float, Bt_strike: float, gamma: float) -> float From glancing angle (gamma) to poloidal angle. :param Bp_strike: Poloidal magnetic field value at the desired point :param Bt_strike: Toroidal magnetic field value at the desired point :param gamma: Glancing angle at the strike point [deg] :rtype: Poloidal angle at the strike point [deg]