예제 #1
0
    def __edge(self, field, sx, sy, diff1, diff2, rec):
        import numpy as np
        from pencil.calc.streamlines import Stream
        from pencil.math.interpolation import vec_int

        phi_min = np.pi / 8.0
        dtot = np.arctan2(
            diff1[0] * diff2[1] - diff2[0] * diff1[1],
            diff1[0] * diff2[0] + diff1[1] * diff2[1],
        )
        if (abs(dtot) > phi_min) and (rec < 4):
            xm = 0.5 * (sx[0] + sx[1])
            ym = 0.5 * (sy[0] + sy[1])

            # Trace the intermediate field line.
            #            time = np.linspace(0, self.params.Lz/np.max(abs(field[2])), 100)
            field_strength_z0 = vec_int(
                np.array([xm, ym, self.params.Oz]),
                field,
                [self.params.dx, self.params.dy, self.params.dz],
                [self.params.Ox, self.params.Oy, self.params.Oz],
                [self.params.nx, self.params.ny, self.params.nz],
                interpolation=self.params.interpolation,
            )
            field_strength_z0 = np.sqrt(np.sum(field_strength_z0 ** 2))
            time = np.linspace(0, 4 * self.params.Lz / field_strength_z0, 500)
            stream = Stream(
                field, self.params, xx=np.array([xm, ym, self.params.Oz]), time=time
            )
            stream_x0 = stream.tracers[0, 0]
            stream_y0 = stream.tracers[0, 1]
            stream_x1 = stream.tracers[-1, 0]
            stream_y1 = stream.tracers[-1, 1]

            diffm = np.array([stream_x1 - stream_x0, stream_y1 - stream_y0])
            if sum(diffm ** 2) != 0:
                diffm = diffm / np.sqrt(sum(diffm ** 2))
            dtot = self.__edge(
                field, [sx[0], xm], [sy[0], ym], diff1, diffm, rec + 1
            ) + self.__edge(field, [xm, sx[1]], [ym, sy[1]], diffm, diff2, rec + 1)
        return dtot
예제 #2
0
    def __grad_field(self, xyz, var, field, dd):
        """
        Compute the gradient if the field at xyz.
        """

        import numpy as np
        from pencil.math.interpolation import vec_int

        gf = np.zeros((3, 3))
        gf[0, :] = (vec_int(xyz + np.array([dd, 0, 0]), var, field) -
                    vec_int(xyz - np.array([dd, 0, 0]), var, field)) / (2 * dd)
        gf[1, :] = (vec_int(xyz + np.array([0, dd, 0]), var, field) -
                    vec_int(xyz - np.array([0, dd, 0]), var, field)) / (2 * dd)
        gf[2, :] = (vec_int(xyz + np.array([0, 0, dd]), var, field) -
                    vec_int(xyz - np.array([0, 0, dd]), var, field)) / (2 * dd)

        return np.matrix(gf)
예제 #3
0
    def __null_point(self, point, var, field):
        import numpy as np
        from pencil.calc.streamlines import Stream
        from pencil.math.interpolation import vec_int

        dl = np.min([var.dx, var.dy]) / 30.0
        it = 0
        # Tracers used to find the fixed point.
        tracers_null = np.zeros((5, 4))
        while True:
            # Trace field lines at original point and for Jacobian.
            # (second order seems to be enough)
            xx = np.zeros((5, 3))
            xx[0, :] = np.array([point[0], point[1], self.params.Oz])
            xx[1, :] = np.array([point[0] - dl, point[1], self.params.Oz])
            xx[2, :] = np.array([point[0] + dl, point[1], self.params.Oz])
            xx[3, :] = np.array([point[0], point[1] - dl, self.params.Oz])
            xx[4, :] = np.array([point[0], point[1] + dl, self.params.Oz])
            for it1 in range(5):
                #                time = time = np.linspace(0, self.params.Lz/np.max(abs(field[2])), 10)
                field_strength_z0 = vec_int(
                    xx[it1, :],
                    field,
                    [var.dx, var.dy, var.dz],
                    [var.x[0], var.y[0], var.z[0]],
                    [len(var.x), len(var.y), len(var.z)],
                    interpolation=self.params.interpolation,
                )
                field_strength_z0 = np.sqrt(np.sum(field_strength_z0 ** 2))
                time = np.linspace(0, 4 * self.params.Lz / field_strength_z0, 500)
                stream = Stream(field, self.params, xx=xx[it1, :], time=time)
                tracers_null[it1, :2] = xx[it1, :2]
                tracers_null[it1, 2:] = stream.tracers[-1, 0:2]

            # Check function convergence.
            ff = np.zeros(2)
            ff[0] = tracers_null[0, 2] - tracers_null[0, 0]
            ff[1] = tracers_null[0, 3] - tracers_null[0, 1]
            if sum(abs(ff)) <= 1e-3 * np.min([self.params.dx, self.params.dy]):
                fixed_point = np.array([point[0], point[1]])
                break

            # Compute the Jacobian.
            fjac = np.zeros((2, 2))
            fjac[0, 0] = (
                (
                    (tracers_null[2, 2] - tracers_null[2, 0])
                    - (tracers_null[1, 2] - tracers_null[1, 0])
                )
                / 2.0
                / dl
            )
            fjac[0, 1] = (
                (
                    (tracers_null[4, 2] - tracers_null[4, 0])
                    - (tracers_null[3, 2] - tracers_null[3, 0])
                )
                / 2.0
                / dl
            )
            fjac[1, 0] = (
                (
                    (tracers_null[2, 3] - tracers_null[2, 1])
                    - (tracers_null[1, 3] - tracers_null[1, 1])
                )
                / 2.0
                / dl
            )
            fjac[1, 1] = (
                (
                    (tracers_null[4, 3] - tracers_null[4, 1])
                    - (tracers_null[3, 3] - tracers_null[3, 1])
                )
                / 2.0
                / dl
            )

            # Invert the Jacobian.
            fjin = np.zeros((2, 2))
            det = fjac[0, 0] * fjac[1, 1] - fjac[0, 1] * fjac[1, 0]
            if abs(det) < dl:
                fixed_point = point
                break
            fjin[0, 0] = fjac[1, 1]
            fjin[1, 1] = fjac[0, 0]
            fjin[0, 1] = -fjac[0, 1]
            fjin[1, 0] = -fjac[1, 0]
            fjin = fjin / det
            dpoint = np.zeros(2)
            dpoint[0] = -fjin[0, 0] * ff[0] - fjin[0, 1] * ff[1]
            dpoint[1] = -fjin[1, 0] * ff[0] - fjin[1, 1] * ff[1]
            point += dpoint

            # Check root convergence.
            if sum(abs(dpoint)) < 1e-3 * np.min([self.params.dx, self.params.dy]):
                fixed_point = point
                print("Root finding converged.")
                break

            if it > 20:
                fixed_point = point
                print("Root finding did not converge.")
                break

            it += 1

        return fixed_point
예제 #4
0
    def find_fixed(
        self,
        datadir="data",
        var_file="VAR0",
        trace_field="bb",
        ti=-1,
        tf=-1,
        tracer_file_name=None,
    ):
        """
        Find the fixed points to a snapshot or existing tracer file.

        call signature::

          find_fixed(datadir='data', var_file='VAR0', trace_field='bb',
                     ti=-1, tf=-1, tracer_file_name=None):

        Keyword arguments:

          *datadir*:
            Data directory.

         *var_file*:
           Varfile to be read.

         *trace_field*:
           Vector field used for the streamline tracing.

          *ti*:
            Initial VAR file index for tracer time sequences. Overrides 'var_file'.

          *tf*:
            Final VAR file index for tracer time sequences. Overrides 'var_file'.

         *tracer_file_name*
           Name of the tracer file to be read.
           If 'None' compute the tracers.
        """

        import numpy as np
        import multiprocessing as mp
        from pencil import read
        from pencil import math
        from pencil.diag.tracers import Tracers
        from pencil.calc.streamlines import Stream
        from pencil.math.interpolation import vec_int

        if self.params.int_q == "curly_A":
            self.curly_A = []
        if self.params.int_q == "ee":
            self.ee = []

        # Multi core setup.
        if not (np.isscalar(self.params.n_proc)) or (self.params.n_proc % 1 != 0):
            print("Error: invalid processor number")
            return -1
        queue = mp.Queue()

        # Make sure to read the var files with the correct magic.
        magic = []
        if trace_field == "bb":
            magic.append("bb")
        if trace_field == "jj":
            magic.append("jj")
        if trace_field == "vort":
            magic.append("vort")
        if self.params.int_q == "ee":
            magic.append("bb")
            magic.append("jj")
        dim = read.dim(datadir=datadir)

        # Check if user wants a tracer time series.
        if (ti % 1 == 0) and (tf % 1 == 0) and (ti >= 0) and (tf >= ti):
            series = True
            var_file = "VAR{0}".format(ti)
            n_times = tf - ti + 1
        else:
            series = False
            n_times = 1
        self.t = np.zeros(n_times)

        # Read the initial field.
        var = read.var(
            var_file=var_file, datadir=datadir, magic=magic, quiet=True, trimall=True
        )

        self.t[0] = var.t
        grid = read.grid(datadir=datadir, quiet=True, trim=True)
        field = getattr(var, trace_field)
        param2 = read.param(datadir=datadir, quiet=True)
        if self.params.int_q == "ee":
            ee = var.jj * param2.eta - math.cross(var.uu, var.bb)
        self.params.datadir = datadir
        self.params.var_file = var_file
        self.params.trace_field = trace_field

        # Get the simulation parameters.
        self.params.dx = var.dx
        self.params.dy = var.dy
        self.params.dz = var.dz
        self.params.Ox = var.x[0]
        self.params.Oy = var.y[0]
        self.params.Oz = var.z[0]
        self.params.Lx = grid.Lx
        self.params.Ly = grid.Ly
        self.params.Lz = grid.Lz
        self.params.nx = dim.nx
        self.params.ny = dim.ny
        self.params.nz = dim.nz

        tracers = Tracers()
        tracers.params = self.params
        # Create the mapping for all times.
        if not tracer_file_name:
            tracers.find_tracers(
                var_file=var_file,
                datadir=datadir,
                trace_field=trace_field,
                ti=ti,
                tf=tf,
            )
        else:
            tracers.read(datadir=datadir, file_name=tracer_file_name)
        self.tracers = tracers

        # Set some default values.
        self.t = np.zeros((tf - ti + 1) * series + (1 - series))
        self.fixed_index = np.zeros((tf - ti + 1) * series + (1 - series))
        self.poincare = np.zeros(
            [
                int(self.params.trace_sub * dim.nx),
                int(self.params.trace_sub * dim.ny),
                n_times,
            ]
        )
        ix0 = range(0, int(self.params.nx * self.params.trace_sub) - 1)
        iy0 = range(0, int(self.params.ny * self.params.trace_sub) - 1)
        self.fixed_points = []
        self.fixed_sign = []
        self.fixed_tracers = []

        # Start the parallelized fixed point finding.
        for tidx in range(n_times):
            if tidx > 0:
                var = read.var(
                    var_file="VAR{0}".format(tidx + ti),
                    datadir=datadir,
                    magic=magic,
                    quiet=True,
                    trimall=True,
                )
                field = getattr(var, trace_field)
                self.t[tidx] = var.t

            proc = []
            sub_data = []
            fixed = []
            fixed_sign = []
            fixed_tracers = []
            for i_proc in range(self.params.n_proc):
                proc.append(
                    mp.Process(
                        target=self.__sub_fixed,
                        args=(queue, ix0, iy0, field, self.tracers, tidx, var, i_proc),
                    )
                )
            for i_proc in range(self.params.n_proc):
                proc[i_proc].start()
            for i_proc in range(self.params.n_proc):
                sub_data.append(queue.get())
            for i_proc in range(self.params.n_proc):
                proc[i_proc].join()
            for i_proc in range(self.params.n_proc):
                # Extract the data from the single cores. Mind the order.
                sub_proc = sub_data[i_proc][0]
                fixed.extend(sub_data[i_proc][1])
                fixed_tracers.extend(sub_data[i_proc][2])
                fixed_sign.extend(sub_data[i_proc][3])
                self.fixed_index[tidx] += sub_data[i_proc][4]
                self.poincare[sub_proc :: self.params.n_proc, :, tidx] = sub_data[
                    i_proc
                ][5]
            for i_proc in range(self.params.n_proc):
                proc[i_proc].terminate()

            # Discard fixed points which lie too close to each other.
            fixed, fixed_tracers, fixed_sign = self.__discard_close_fixed_points(
                np.array(fixed), np.array(fixed_sign), np.array(fixed_tracers), var
            )
            if self.fixed_points is None:
                self.fixed_points = []
                self.fixed_sign = []
                self.fixed_tracers = []
            self.fixed_points.append(np.array(fixed))
            self.fixed_sign.append(np.array(fixed_sign))
            self.fixed_tracers.append(fixed_tracers)

        # Compute the traced quantities along the fixed point streamlines.
        if (self.params.int_q == "curly_A") or (self.params.int_q == "ee"):
            for t_idx in range(0, n_times):
                if self.params.int_q == "curly_A":
                    self.curly_A.append([])
                if self.params.int_q == "ee":
                    self.ee.append([])
                for fixed in self.fixed_points[t_idx]:
                    # Trace the stream line.
                    xx = np.array([fixed[0], fixed[1], self.params.Oz])
                    #                    time = np.linspace(0, self.params.Lz/np.max(abs(field[2])), 10)
                    field_strength_z0 = vec_int(
                        xx,
                        field,
                        [var.dx, var.dy, var.dz],
                        [var.x[0], var.y[0], var.z[0]],
                        [len(var.x), len(var.y), len(var.z)],
                        interpolation=self.params.interpolation,
                    )
                    field_strength_z0 = np.sqrt(np.sum(field_strength_z0 ** 2))
                    time = np.linspace(0, 4 * self.params.Lz / field_strength_z0, 500)
                    stream = Stream(field, self.params, xx=xx, time=time)
                    # Do the field line integration.
                    if self.params.int_q == "curly_A":
                        curly_A = 0
                        for l in range(stream.iterations - 1):
                            aaInt = vec_int(
                                (stream.tracers[l + 1] + stream.tracers[l]) / 2,
                                var.aa,
                                [var.dx, var.dy, var.dz],
                                [var.x[0], var.y[0], var.z[0]],
                                [len(var.x), len(var.y), len(var.z)],
                                interpolation=self.params.interpolation,
                            )
                            curly_A += np.dot(
                                aaInt, (stream.tracers[l + 1] - stream.tracers[l])
                            )
                        self.curly_A[-1].append(curly_A)
                    if self.params.int_q == "ee":
                        ee_p = 0
                        for l in range(stream.iterations - 1):
                            eeInt = vec_int(
                                (stream.tracers[l + 1] + stream.tracers[l]) / 2,
                                ee,
                                [var.dx, var.dy, var.dz],
                                [var.x[0], var.y[0], var.z[0]],
                                [len(var.x), len(var.y), len(var.z)],
                                interpolation=self.params.interpolation,
                            )
                            ee_p += np.dot(
                                eeInt, (stream.tracers[l + 1] - stream.tracers[l])
                            )
                        self.ee[-1].append(ee_p)
                if self.params.int_q == "curly_A":
                    self.curly_A[-1] = np.array(self.curly_A[-1])
                if self.params.int_q == "ee":
                    self.ee[-1] = np.array(self.ee[-1])

        return 0
예제 #5
0
    def __sub_fixed(self, queue, ix0, iy0, field, tracers, tidx, var, i_proc):
        import numpy as np
        from pencil.calc.streamlines import Stream
        from pencil.math.interpolation import vec_int

        diff = np.zeros((4, 2))
        fixed = []
        fixed_sign = []
        fixed_tracers = []
        fixed_index = 0
        poincare_array = np.zeros(
            (tracers.x0[i_proc :: self.params.n_proc].shape[0], tracers.x0.shape[1])
        )

        for ix in ix0[i_proc :: self.params.n_proc]:
            for iy in iy0:
                # Compute Poincare index around this cell (!= 0 for potential fixed point).
                diff[0, :] = np.array(
                    [
                        tracers.x1[ix, iy, tidx] - tracers.x0[ix, iy, tidx],
                        tracers.y1[ix, iy, tidx] - tracers.y0[ix, iy, tidx],
                    ]
                )
                diff[1, :] = np.array(
                    [
                        tracers.x1[ix + 1, iy, tidx] - tracers.x0[ix + 1, iy, tidx],
                        tracers.y1[ix + 1, iy, tidx] - tracers.y0[ix + 1, iy, tidx],
                    ]
                )
                diff[2, :] = np.array(
                    [
                        tracers.x1[ix + 1, iy + 1, tidx]
                        - tracers.x0[ix + 1, iy + 1, tidx],
                        tracers.y1[ix + 1, iy + 1, tidx]
                        - tracers.y0[ix + 1, iy + 1, tidx],
                    ]
                )
                diff[3, :] = np.array(
                    [
                        tracers.x1[ix, iy + 1, tidx] - tracers.x0[ix, iy + 1, tidx],
                        tracers.y1[ix, iy + 1, tidx] - tracers.y0[ix, iy + 1, tidx],
                    ]
                )
                if sum(np.sum(diff ** 2, axis=1) != 0):
                    diff = np.swapaxes(
                        np.swapaxes(diff, 0, 1) / np.sqrt(np.sum(diff ** 2, axis=1)),
                        0,
                        1,
                    )
                poincare = self.__poincare_index(
                    field,
                    tracers.x0[ix : ix + 2, iy, tidx],
                    tracers.y0[ix, iy : iy + 2, tidx],
                    diff,
                )
                poincare_array[ix // self.params.n_proc, iy] = poincare

                if (
                    abs(poincare) > 5
                ):  # Use 5 instead of 2*pi to account for rounding errors.
                    # Subsample to get starting point for iteration.
                    nt = 2
                    xmin = tracers.x0[ix, iy, tidx]
                    ymin = tracers.y0[ix, iy, tidx]
                    xmax = tracers.x0[ix + 1, iy, tidx]
                    ymax = tracers.y0[ix, iy + 1, tidx]
                    xx = np.zeros((nt ** 2, 3))
                    tracers_part = np.zeros((nt ** 2, 5))
                    i1 = 0
                    for j1 in range(nt):
                        for k1 in range(nt):
                            xx[i1, 0] = xmin + j1 / (nt - 1.0) * (xmax - xmin)
                            xx[i1, 1] = ymin + k1 / (nt - 1.0) * (ymax - ymin)
                            xx[i1, 2] = self.params.Oz
                            i1 += 1
                    for it1 in range(nt ** 2):
                        #                        time = np.linspace(0, self.params.Lz/np.max(abs(field[2])), 10)
                        field_strength_z0 = vec_int(
                            xx[it1, :],
                            field,
                            [var.dx, var.dy, var.dz],
                            [var.x[0], var.y[0], var.z[0]],
                            [len(var.x), len(var.y), len(var.z)],
                            interpolation=self.params.interpolation,
                        )
                        field_strength_z0 = np.sqrt(np.sum(field_strength_z0 ** 2))
                        time = np.linspace(
                            0, 4 * self.params.Lz / field_strength_z0, 500
                        )
                        stream = Stream(field, self.params, xx=xx[it1, :], time=time)
                        tracers_part[it1, 0:2] = xx[it1, 0:2]
                        tracers_part[it1, 2:] = stream.tracers[-1, :]
                    min2 = 1e6
                    minx = xmin
                    miny = ymin
                    i1 = 0
                    for j1 in range(nt):
                        for k1 in range(nt):
                            diff2 = (
                                tracers_part[i1 + k1 * nt, 2]
                                - tracers_part[i1 + k1 * nt, 0]
                            ) ** 2 + (
                                tracers_part[i1 + k1 * nt, 3]
                                - tracers_part[i1 + k1 * nt, 1]
                            ) ** 2
                            if diff2 < min2:
                                min2 = diff2
                                minx = xmin + j1 / (nt - 1.0) * (xmax - xmin)
                                miny = ymin + k1 / (nt - 1.0) * (ymax - ymin)

                    # Get fixed point from this starting position using Newton's method.
                    point = np.array([minx, miny])
                    fixed_point = self.__null_point(point, var, field)

                    # Check if fixed point lies outside the cell.
                    if (
                        (fixed_point[0] < tracers.x0[ix, iy, tidx])
                        or (fixed_point[0] > tracers.x0[ix + 1, iy, tidx])
                        or (fixed_point[1] < tracers.y0[ix, iy, tidx])
                        or (fixed_point[1] > tracers.y0[ix, iy + 1, tidx])
                    ):
                        fixed_point[0] = minx
                        fixed_point[1] = miny
                    #                    else:
                    fixed.append(fixed_point)
                    fixed_sign.append(np.sign(poincare))
                    fixed_index += np.sign(poincare)

                    # Find the streamline at the fixed point.
                    #                    time = np.linspace(0, self.params.Lz/np.max(abs(field[2])), 100)
                    field_strength_z0 = vec_int(
                        np.array([fixed_point[0], fixed_point[1], self.params.Oz]),
                        field,
                        [var.dx, var.dy, var.dz],
                        [var.x[0], var.y[0], var.z[0]],
                        [len(var.x), len(var.y), len(var.z)],
                        interpolation=self.params.interpolation,
                    )
                    field_strength_z0 = np.sqrt(np.sum(field_strength_z0 ** 2))
                    time = np.linspace(0, 4 * self.params.Lz / field_strength_z0, 500)
                    stream = Stream(
                        field,
                        self.params,
                        xx=np.array([fixed_point[0], fixed_point[1], self.params.Oz]),
                        time=time,
                    )
                    fixed_tracers.append(stream.tracers)

        queue.put(
            (i_proc, fixed, fixed_tracers, fixed_sign, fixed_index, poincare_array)
        )
예제 #6
0
    def find_separatrices(
        self, var, field, null_point, delta=0.1, iter_max=100, ring_density=8
    ):
        """
        find_separatrices(var, field, null_point, delta=0.1,
                          iter_max=100, ring_density=8)

        Find the separatrices to the field 'field' with information from 'var'.

        Parameters
        ----------
        var : obj
            The var object from the read_var routine.

        field : string
            The vector field.

        null_point : obj
            NullPoint object containing the magnetic null points.

        delta : float
            Step length for the field line tracing.

        iter_max : int
            Maximum iteration steps for the fiel line tracing.

        ring_density : float
            Density of the tracer rings.
        """

        import numpy as np
        from pencil.math.interpolation import vec_int

        separatrices = []
        connectivity = []
        for null_idx in range(len(null_point.nulls)):
            null = null_point.nulls[null_idx]
            normal = null_point.normals[null_idx]
            fan_vectors = null_point.fan_vectors[null_idx]
            sign_trace = null_point.sign_trace[null_idx]

            tracing = True
            separatrices.append(null)

            # Only trace separatrices for x-point lilke nulls.
            if abs(np.linalg.det(null_point.eigen_vectors[null_idx])) < delta * 1e-8:
                continue

            # Create the first ring of points.
            ring = []
            offset = len(separatrices) - 1
            for theta in np.linspace(
                0, 2 * np.pi * (1 - 1.0 / ring_density), ring_density
            ):
                ring.append(
                    null + self.__rotate_vector(normal, fan_vectors[0], theta) * delta
                )
                separatrices.append(ring[-1])
                # Set the connectivity with the null point.
                connectivity.append(np.array([0, len(ring)]) + offset)

            # Set the connectivity within the ring.
            for idx in range(ring_density - 1):
                connectivity.append(np.array([idx + 1, idx + 2]) + offset)
            connectivity.append(np.array([1, ring_density]) + offset)

            # Trace the rings around the null.
            iteration = 0
            while tracing and iteration < iter_max:
                ring_old = ring

                # Trace field lines on ring.
                point_idx = 0
                for point in ring:
                    field_norm = vec_int(point, var, field) * sign_trace
                    field_norm = field_norm / np.sqrt(np.sum(field_norm ** 2))
                    point = point + field_norm * delta
                    ring[point_idx] = point
                    point_idx += 1

                # Connectivity array between old and new ring.
                connectivity_rings = np.ones((2, len(ring)), dtype="int") * range(
                    len(ring)
                )

                # Add points if distance becomes too large.
                ring_new = []
                ring_new.append(ring[0])
                for point_idx in range(len(ring) - 1):
                    if self.__distance(ring[point_idx], ring[point_idx + 1]) > delta:
                        ring_new.append((ring[point_idx] + ring[point_idx + 1]) / 2)
                        connectivity_rings[1, point_idx + 1 :] += 1
                    ring_new.append(ring[point_idx + 1])
                if self.__distance(ring[0], ring[-1]) > delta:
                    ring_new.append((ring[0] + ring[-1]) / 2)
                ring = ring_new

                # Remove points which lie outside.
                ring_new = []
                not_connect_to_next = []
                left_shift = np.zeros(connectivity_rings.shape[1])
                for point_idx in range(len(ring)):
                    if self.__inside_domain(ring[point_idx], var):
                        ring_new.append(ring[point_idx])
                        separatrices.append(ring[point_idx])
                    else:
                        not_connect_to_next.append(len(ring_new) - 1)
                        mask = connectivity_rings[1, :] == point_idx
                        connectivity_rings[1, mask] = -1
                        mask = connectivity_rings[1, :] > point_idx
                        left_shift += mask
                connectivity_rings[1, :] -= left_shift
                ring = ring_new

                # Stop the tracing routine if there are no points in the ring.
                if not ring:
                    tracing = False
                    continue

                # Set the connectivity within the ring.
                offset = len(separatrices) - len(ring_new)
                for point_idx in range(len(ring_new) - 1):
                    if not np.any(np.array(not_connect_to_next) == point_idx):
                        connectivity.append(
                            np.array([offset + point_idx, offset + point_idx + 1])
                        )
                if not np.any(
                    np.array(not_connect_to_next) == len(ring_new)
                ) and not np.any(np.array(not_connect_to_next) == -1):
                    connectivity.append(np.array([offset, offset + len(ring_new) - 1]))

                # Set the connectivity between the old and new ring.
                for point_old_idx in range(len(ring_old)):
                    if connectivity_rings[1, point_old_idx] >= 0:
                        connectivity_rings[0, point_old_idx] += (
                            len(separatrices) - len(ring_old) - len(ring)
                        )
                        connectivity_rings[1, point_old_idx] += len(separatrices) - len(
                            ring
                        )
                        connectivity.append(
                            np.array(
                                [
                                    connectivity_rings[0, point_old_idx],
                                    connectivity_rings[1, point_old_idx],
                                ]
                            )
                        )

                iteration += 1

        self.separatrices = np.array(separatrices)
        self.connectivity = np.array(connectivity)
예제 #7
0
    def find_spines(self, var, field, null_point, delta=0.1, iter_max=100):
        """
        find_spines(var, field, null_point, delta=0.1, iter_max=100)

        Find the spines to the field 'field' with information from 'var'.


        Parameters
        ----------
        var : obj
            The var object from the read_var routine.

        field : string
            The vector field.

        null_point : obj
            NullPoint object containing the magnetic null points.

        delta : float
            Step length for the field line tracing.

        iter_max : int
            Maximum iteration steps for the fiel line tracing.
        """

        import numpy as np
        from pencil.math.interpolation import vec_int

        spines = []
        for null_idx in range(len(null_point.nulls)):
            field_sgn = -field * null_point.sign_trace[null_idx]
            null = null_point.nulls[null_idx]
            spine_up = []
            spine_down = []
            spine_up.append(null)
            spine_down.append(null)

            # Trace spine above the null.
            iteration = 0
            point = null + null_point.normals[null_idx] * delta
            tracing = True
            iteration = 0
            while tracing and iteration < iter_max:
                spine_up.append(point)
                field_norm = vec_int(point, var, field_sgn)
                field_norm = field_norm / np.sqrt(np.sum(field_norm ** 2))
                point = point + field_norm * delta
                if not self.__inside_domain(point, var):
                    tracing = False
                iteration += 1
            spines.append(np.array(spine_up))

            # Trace spine below the null.
            iteration = 0
            point = null - null_point.normals[null_idx] * delta
            tracing = True
            iteration = 0
            while tracing and iteration < iter_max:
                spine_down.append(point)
                field_norm = vec_int(point, var, field_sgn)
                field_norm = field_norm / np.sqrt(np.sum(field_norm ** 2))
                point = point + field_norm * delta
                if not self.__inside_domain(point, var):
                    tracing = False
                iteration += 1
            spines.append(np.array(spine_down))
        self.spines = np.array(spines)
예제 #8
0
    def __init__(
        self, field, params, xx=(0, 0, 0), time=(0, 1), metric=None, splines=None
    ):
        """
        Trace a field starting from xx in any rectilinear coordinate system
        with constant dx, dy and dz and with a given metric.

        call signature:

          tracer(field, params, xx=[0, 0, 0], time=[0, 1], metric=None, splines=None):

        Keyword arguments:

        *field*:
          Vector field which is integrated over with shape [n_vars, nz, ny, nx].
          Its elements are the components of the field using unnormed
          unit-coordinate vectors.

        *params*:
          Simulation and tracer parameters.

        *xx*:
          Starting point of the field line integration with starting time.

        *time*:
            Time array for which the tracer is computed.

        *metric*:
            Metric function that takes a point [x, y, z] and an array
            of shape [3, 3] that has the comkponents g_ij.
            Use 'None' for Cartesian metric.

        *splines*:
            Spline interpolation functions for the tricubic interpolation.
            This can speed up the calculations greatly for repeated streamline
            tracing on the same data.
            Accepts a list of the spline functions for the three vector components.
        """

        import numpy as np
        from scipy.integrate import ode
        from scipy.integrate import solve_ivp
        from pencil.math.interpolation import vec_int

        if params.interpolation == "tricubic":
            try:
                import warnings

                with warnings.catch_warnings():
                    warnings.filterwarnings("ignore", category=Warning)
                    from eqtools.trispline import Spline
            except:
                print(
                    "Warning: Could not import eqtools.trispline.Spline for tricubic interpolation.\n"
                )
                print("Warning: Fall back to trilinear.")
                params.interpolation = "trilinear"

        if not metric:
            metric = lambda xx: np.eye(3)

        dxyz = np.array([params.dx, params.dy, params.dz])
        oxyz = np.array([params.Ox, params.Oy, params.Oz])
        nxyz = np.array([params.nx, params.ny, params.nz])

        # Redefine the derivative y for the scipy ode integrator using the given parameters.
        if (params.interpolation == "mean") or (params.interpolation == "trilinear"):
            odeint_func = lambda t, xx: vec_int(
                xx, field, dxyz, oxyz, nxyz, params.interpolation
            )
        if params.interpolation == "tricubic":
            x = np.linspace(params.Ox, params.Ox + params.Lx, params.nx)
            y = np.linspace(params.Oy, params.Oy + params.Ly, params.ny)
            z = np.linspace(params.Oz, params.Oz + params.Lz, params.nz)
            if splines is None:
                field_x = Spline(z, y, x, field[0, ...])
                field_y = Spline(z, y, x, field[1, ...])
                field_z = Spline(z, y, x, field[2, ...])
            else:
                field_x = splines[0]
                field_y = splines[1]
                field_z = splines[2]
            odeint_func = lambda t, xx: self.trilinear_func(
                xx, field_x, field_y, field_z, params
            )
            del x
            del y
            del z
        # Set up the ode solver.
        methods_ode = ["vode", "zvode", "lsoda", "dopri5", "dop853"]
        methods_ivp = ["RK45", "RK23", "Radau", "BDF", "LSODA"]
        if params.method in methods_ode:
            solver = ode(odeint_func, jac=metric)
            solver.set_initial_value(xx, time[0])
            solver.set_integrator(params.method, rtol=params.rtol, atol=params.atol)
            self.tracers = np.zeros([len(time), 3])
            self.tracers[0, :] = xx
            for i, t in enumerate(time[1:]):
                self.tracers[i + 1, :] = solver.integrate(t)
        if params.method in methods_ivp:
            self.tracers = solve_ivp(
                odeint_func,
                (time[0], time[-1]),
                xx,
                t_eval=time,
                rtol=params.rtol,
                atol=params.atol,
                method=params.method,
            ).y.T

        # Remove points that lie outside the domain and interpolation on the boundary.
        cut_mask = (
            (
                (self.tracers[:, 0] > params.Ox + params.Lx)
                + (self.tracers[:, 0] < params.Ox)
            )
            * (not params.periodic_x)
            + (
                (self.tracers[:, 1] > params.Oy + params.Ly)
                + (self.tracers[:, 1] < params.Oy)
            )
            * (not params.periodic_y)
            + (
                (self.tracers[:, 2] > params.Oz + params.Lz)
                + (self.tracers[:, 2] < params.Oz)
            )
            * (not params.periodic_z)
        )
        if np.sum(cut_mask) > 0:
            # Find the first point that lies outside.
            idx_outside = np.min(np.where(cut_mask))
            # Interpolate.
            p0 = self.tracers[idx_outside - 1, :]
            p1 = self.tracers[idx_outside, :]
            lam = np.zeros([6])
            if p0[0] == p1[0]:
                lam[0] = np.inf
                lam[1] = np.inf
            else:
                lam[0] = (params.Ox + params.Lx - p0[0]) / (p1[0] - p0[0])
                lam[1] = (params.Ox - p0[0]) / (p1[0] - p0[0])
            if p0[1] == p1[1]:
                lam[2] = np.inf
                lam[3] = np.inf
            else:
                lam[2] = (params.Oy + params.Ly - p0[1]) / (p1[1] - p0[1])
                lam[3] = (params.Oy - p0[1]) / (p1[1] - p0[1])
            if p0[2] == p1[2]:
                lam[4] = np.inf
                lam[5] = np.inf
            else:
                lam[4] = (params.Oz + params.Lz - p0[2]) / (p1[2] - p0[2])
                lam[5] = (params.Oz - p0[2]) / (p1[2] - p0[2])
            lam_min = np.min(lam[lam >= 0])
            if abs(lam_min) == np.inf:
                lam_min = 0
            self.tracers[idx_outside, :] = p0 + lam_min * (p1 - p0)
            # We don't want to cut the interpolated point (was first point outside).
            cut_mask[idx_outside] = False
            cut_mask[idx_outside + 1 :] = True
            # Remove outside points.
            self.tracers = self.tracers[~cut_mask, :].copy()

        self.params = params
        self.xx = xx
        self.time = time

        # Compute the length of the line segments.
        middle_point_list = (self.tracers[1:, :] + self.tracers[:-1, :]) / 2
        diff_vectors = self.tracers[1:, :] - self.tracers[:-1, :]
        self.section_l = np.zeros(diff_vectors.shape[0])
        for idx, middle_point in enumerate(middle_point_list):
            self.section_l[idx] = np.sqrt(
                np.sum(
                    diff_vectors[idx]
                    * np.sum(metric(middle_point) * diff_vectors[idx], axis=1)
                )
            )
        self.total_l = np.sum(self.section_l)

        self.iterations = len(time)
        self.section_dh = time[1:] - time[:-1]
        self.total_h = time[-1] - time[0]
예제 #9
0
    def __sub_tracers(self, queue, field, t_idx, i_proc, n_proc):
        import numpy as np
        from pencil.calc.streamlines import Stream
        from pencil.math.interpolation import vec_int

        # Prepare the splines for the tricubis interpolation.
        if self.params.interpolation == 'tricubic':
            try:
                from eqtools.trispline import Spline
                x = np.linspace(self.params.Ox, self.params.Ox+self.params.Lx, self.params.nx)
                y = np.linspace(self.params.Oy, self.params.Oy+self.params.Ly, self.params.ny)
                z = np.linspace(self.params.Oz, self.params.Oz+self.params.Lz, self.params.nz)
                field_x = Spline(z, y, x, field[0, ...])
                field_y = Spline(z, y, x, field[1, ...])
                field_z = Spline(z, y, x, field[2, ...])
                splines = np.array([field_x, field_y, field_z])
            except:
                splines = None
        else:
            splines = None

        xx = np.zeros([(self.x0.shape[0]+n_proc-1-i_proc)//n_proc,
                       self.x0.shape[1], 3])
        xx[:, :, 0] = self.x0[i_proc:self.x0.shape[0]:n_proc, :, t_idx].copy()
        xx[:, :, 1] = self.y0[i_proc:self.x0.shape[0]:n_proc, :, t_idx].copy()
        xx[:, :, 2] = self.z1[i_proc:self.x0.shape[0]:n_proc, :, t_idx].copy()

        # Initialize the local arrays for this core.
        sub_x1 = np.zeros(xx[:, :, 0].shape)
        sub_y1 = np.zeros(xx[:, :, 0].shape)
        sub_z1 = np.zeros(xx[:, :, 0].shape)
        sub_l = np.zeros(xx[:, :, 0].shape)
        sub_curly_A = np.zeros(xx[:, :, 0].shape)
        sub_ee = np.zeros(xx[:, :, 0].shape)
        sub_mapping = np.zeros([xx[:, :, 0].shape[0], xx[:, :, 0].shape[1], 3])
        for ix in range(i_proc, self.x0.shape[0], n_proc):
            for iy in range(self.x0.shape[1]):
                time = np.linspace(0, 20*self.params.Lz/field[2, 0, iy, ix], 1000)
                stream = Stream(field, self.params, xx=xx[int(ix/n_proc), iy, :],
                                time=time, splines=splines)
                sub_x1[int(ix/n_proc), iy] = stream.tracers[-1, 0]
                sub_y1[int(ix/n_proc), iy] = stream.tracers[-1, 1]
                sub_z1[int(ix/n_proc), iy] = stream.tracers[-1, 2]
                sub_l[int(ix/n_proc), iy] = stream.total_l
                if self.params.int_q == 'curly_A':
                    for l in range(stream.total_l):
                        aaInt = vec_int((stream.tracers[l+1] + stream.tracers[l])/2, self.aa,
                                        [self.params.dx, self.params.dy, self.params.dz],
                                        [self.params.Ox, self.params.Oy, self.params.Oz],
                                        [self.params.nx, self.params.ny, self.params.nz],
                                        interpolation=self.params.interpolation)
                        sub_curly_A[int(ix/n_proc), iy] += \
                            np.dot(aaInt, (stream.tracers[l+1] - stream.tracers[l]))
                if self.params.int_q == 'ee':
                    for l in range(stream.total_l):
                        eeInt = vec_int((stream.tracers[l+1] + stream.tracers[l])/2, self.ee,
                                        [self.params.dx, self.params.dy, self.params.dz],
                                        [self.params.Ox, self.params.Oy, self.params.Oz],
                                        [self.params.nx, self.params.ny, self.params.nz],
                                        interpolation=self.params.interpolation)
                        sub_ee[int(ix/n_proc), iy] += \
                            np.dot(eeInt, (stream.tracers[l+1] - stream.tracers[l]))

                # Create the color mapping.
#                if (sub_z1[int(ix/n_proc), iy] > self.params.Oz+self.params.Lz-self.params.dz*12):
                if (self.x0[ix, iy, t_idx] - sub_x1[int(ix/n_proc), iy]) > 0:
                    if (self.y0[ix, iy, t_idx] - sub_y1[int(ix/n_proc), iy]) > 0:
                        sub_mapping[int(ix/n_proc), iy, :] = [0, 1, 0]
                    else:
                        sub_mapping[int(ix/n_proc), iy, :] = [1, 1, 0]
                else:
                    if (self.y0[ix, iy, t_idx] - sub_y1[int(ix/n_proc), iy]) > 0:
                        sub_mapping[int(ix/n_proc), iy, :] = [0, 0, 1]
                    else:
                        sub_mapping[int(ix/n_proc), iy, :] = [1, 0, 0]
#                else:
#                    sub_mapping[int(ix/n_proc), iy, :] = [1, 1, 1]

        queue.put((i_proc, sub_x1, sub_y1, sub_z1, sub_l, sub_mapping,
                   sub_curly_A, sub_ee))