aboutsummaryrefslogtreecommitdiffstats
path: root/Helpers/chaos_helpers.py
blob: 1859b53b4a5109a20a4b4cbd8de17988d403d4a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""
Helper Functions of Chaos Characterization.
"""

import numpy as np
from array_helpers import intersect2d
from g2_helpers import twopi

def co_intersection(cpid, opid, cm, om):
    """
    Current/Old Intersection.

    Compute Intersection of Indices and Masses.

    @params
    cpid = array of current particle IDs
    opid =          old
    cm   = array of current particle masses
    co   =          old

    @returns
    subset of particle IDs where IDs and masses match
    """

    current = np.transpose(np.array([cpid, cm])).copy()
    old = np.transpose(np.array([opid, om])).copy()
    return intersect2d(current, old)[:,0].astype(int)

def compute_lyapunov(ds, istep0, nsteps, tout):
    """
    Compute Lyapunov Exponents.

    @params
    ds = [0:nsteps,0:npartmax]      Particle separation
    nstep0 = [0,npartmax]           First step where separation occured
    t = [0,nsteps]                  Time array

    @returns
    lce = [0:nsteps,0:npartmax]
    """

    lce = np.ones_like(ds) * np.nan
    for istep, nstep in enumerate(nsteps):
        for ipart in range(ds.shape[1]):
            # Only compute if we are above t where first separation occured
            if istep > istep0[ipart]:
                # Compute lambda wrt to last step
                lce[istep,ipart] = np.log(ds[istep,ipart] / ds[istep0[ipart],ipart]) / \
                                   ( tout[istep] - tout[istep0[ipart]] )
                # Normalization foo?
                # ...

    return lce

def bin_lyapunov(a0, lce):
    """
    Create bins in semi-major axis.
    Then sort Lyapunov Characteristics Exponent (LCE) into bins.
    Per bin, compute median, mean, and standard deviation of LCE.
    Save.

    @parameters - lce - LCE(a0,t)
                - a0  - Initial semi-major axes of particles
    @return     - lce_med       - Mean value per bin ~ lce(a0i,t)
                - lce_median    - Median per bin
                - lce_std       - Standard deviation per bin
                - a0_bin_mids   - Bin midpoints
    """

    # Set up bins
    nbins = 32; a0_lo = 0.0; a0_hi = 5.5

    # Create bins
    a0_bin_edges = np.linspace(a0_lo, a0_hi, nbins+1)           # len=nbins+1
    a0_bin_mids = (a0_bin_edges[1:] + a0_bin_edges[:-1]) / 2.0  # len=nbins

    # Digitize
    # Cf. http://docs.scipy.org/doc/numpy/reference/generated/numpy.digitize.html#numpy.digitize
    digitalism = np.digitize(a0, a0_bin_edges)
    digitalism -= 1

    #   - Some Error Checking
    if len(a0_bin_edges)-1 in digitalism:
        raise Exception("Particle Outside Binning Range (a0 > a_hi). Adjust Limits!")

    # Allocate target arrays
    lce_mean = np.zeros([lce.shape[0], a0_bin_mids.shape[0]])
    lce_median = np.zeros_like(lce_mean)
    lce_std = np.zeros_like(lce_mean)

    # Sweep arrays first by time, then by particle
    # Compute mean/median/std and write back
    for itslice in range(0,lce.shape[0]):
        lce_binned = [[] for _ in range(0,nbins)]
        for ipslice in range(0,lce.shape[1]):
            lce_binned[digitalism[ipslice]].append(lce[itslice,ipslice])
        for ibin in range(0,nbins):
            if len(lce_binned[ibin]) == 0:
                lce_mean[itslice,ibin] = np.nan
                lce_median[itslice,ibin]= np.nan
                lce_std[itslice,ibin] = np.nan
            else:
                lce_mean[itslice,ibin] = np.mean(lce_binned[ibin])
                lce_median[itslice,ibin] = np.median(lce_binned[ibin])
                lce_std[itslice,ibin] = np.std(lce_binned[ibin])

    # Return values
    return lce_mean, lce_median, lce_std, a0_bin_mids