Ejemplo n.º 1
0
def fa(m0, n0, zs_norm, thetas, funcnum=2):
    """Calculates the matrix with the base functions for `w_0`

    The calculated matrix is directly used to calculate the `w_0` displacement
    field, when the corresponding coefficients `c_0` are known, through::

        a = fa(m0, n0, zs_norm, thetas, funcnum)
        w0 = a.dot(c0)

    Parameters
    ----------
    m0 : int
        The number of terms along the meridian.
    n0 : int
        The number of terms along the circumference.
    zs_norm : np.ndarray
        The normalized `z` coordinates (from 0. to 1.) used to compute
        the base functions.
    thetas : np.ndarray
        The angles in radians representing the circumferential positions.
    funcnum : int, optional
        The function used for the approximation (see function :func:`.calc_c0`)

    """
    try:
        import _fit_data
        return _fit_data.fa(m0, n0, zs_norm, thetas, funcnum)
    except:
        warn('_fit_data.pyx could not be imported, executing in Python/NumPy'
                + '\n\t\tThis mode is slower and needs more memory than the'
                + '\n\t\tPython/NumPy/Cython mode',
             level=1)
        zs = zs_norm.ravel()
        ts = thetas.ravel()
        n = zs.shape[0]
        zsmin = zs.min()
        zsmax = zs.max()
        if zsmin < 0 or zsmax > 1:
            log('zs.min()={0}'.format(zsmin))
            log('zs.max()={0}'.format(zsmax))
            raise ValueError('The zs array must be normalized!')
        if funcnum==1:
            a = np.array([[sin(i*pi*zs)*sin(j*ts), sin(i*pi*zs)*cos(j*ts)]
                           for j in range(n0) for i in range(1, m0+1)])
            a = a.swapaxes(0,2).swapaxes(1,2).reshape(n,-1)
        elif funcnum==2:
            a = np.array([[cos(i*pi*zs)*sin(j*ts), cos(i*pi*zs)*cos(j*ts)]
                           for j in range(n0) for i in range(m0)])
            a = a.swapaxes(0,2).swapaxes(1,2).reshape(n,-1)
        elif funcnum==3:
            a = np.array([[sin(i*pi*zs)*sin(j*ts), sin(i*pi*zs)*cos(j*ts),
                           cos(i*pi*zs)*sin(j*ts), cos(i*pi*zs)*cos(j*ts)]
                           for j in range(n0) for i in range(m0)])
            a = a.swapaxes(0,2).swapaxes(1,2).reshape(n,-1)
    return a
Ejemplo n.º 2
0
def fw0(m0, n0, c0, xs_norm, ts, funcnum=2):
    r"""Calculates the imperfection field `w_0` for a given input

    Parameters
    ----------
    m0 : int
        The number of terms along the meridian.
    n0 : int
        The number of terms along the circumference.
    c0 : np.ndarray
        The coefficients of the imperfection pattern.
    xs_norm : np.ndarray
        The meridian coordinate (`x`) normalized to be between ``0.`` and
        ``1.``.
    ts : np.ndarray
        The angles in radians representing the circumferential coordinate
        (`\theta`).
    funcnum : int, optional
        The function used for the approximation (see function :func:`.calc_c0`)

    Returns
    -------
    w0s : np.ndarray
        An array with the same shape of ``xs_norm`` containing the calculated
        imperfections.

    Notes
    -----
    The inputs ``xs_norm`` and ``ts`` must be of the same size.

    The inputs must satisfy ``c0.shape[0] == size*m0*n0``, where:

    - ``size=2`` if ``funcnum==1 or funcnum==2``
    - ``size=4`` if ``funcnum==3``

    """
    if xs_norm.shape != ts.shape:
        raise ValueError('xs_norm and ts must have the same shape')
    if funcnum==1:
        size = 2
    elif funcnum==2:
        size = 2
    elif funcnum==3:
        size = 4
    if c0.shape[0] != size*m0*n0:
        raise ValueError('Invalid c0 for the given m0 and n0!')
    try:
        import _fit_data
        w0s = _fit_data.fw0(m0, n0, c0, xs_norm.ravel(), ts.ravel(), funcnum)
    except:
        a = fa(m0, n0, xs_norm.ravel(), ts.ravel(), funcnum)
        w0s = a.dot(c0)
    return w0s.reshape(xs_norm.shape)
Ejemplo n.º 3
0
def calc_c0(path, m0=50, n0=50, funcnum=2, fem_meridian_bot2top=True,
        rotatedeg=None, filter_m0=None, filter_n0=None, sample_size=None,
        maxmem=8):
    r"""Find the coefficients that best fit the `w_0` imperfection

    The measured data will be fit using one of the following functions,
    selected using the ``funcnum`` parameter:

    1) Half-Sine Function

    .. math::
        w_0 = \sum_{i=1}^{m_0}{ \sum_{j=0}^{n_0}{
                 {c_0}_{ij}^a sin{b_z} sin{b_\theta}
                +{c_0}_{ij}^b sin{b_z} cos{b_\theta} }}

    2) Half-Cosine Function (default)

    .. math::
        w_0 = \sum_{i=0}^{m_0}{ \sum_{j=0}^{n_0}{
                {c_0}_{ij}^a cos{b_z} sin{b_\theta}
                +{c_0}_{ij}^b cos{b_z} cos{b_\theta} }}

    3) Complete Fourier Series

    .. math::
        w_0 = \sum_{i=0}^{m_0}{ \sum_{j=0}^{n_0}{
                 {c_0}_{ij}^a sin{b_z} sin{b_\theta}
                +{c_0}_{ij}^b sin{b_z} cos{b_\theta}
                +{c_0}_{ij}^c cos{b_z} sin{b_\theta}
                +{c_0}_{ij}^d cos{b_z} cos{b_\theta} }}

    where:

    .. math::
        b_z = i \pi \frac z H_{points}

        b_\theta = j \theta

    where `H_{points}` represents the difference between the maximum and
    the minimum `z` values in the imperfection file.

    The approximation can be written in matrix form as:

    .. math::
        w_0 = [g] \{c_0\}

    where `[g]` carries the base functions and `{c_0}` the respective
    amplitudes. The solution consists on finding the best `{c_0}` that
    minimizes the least-square error between the measured imperfection pattern
    and the `w_0` function.

    Parameters
    ----------
    path : str or np.ndarray
        The path of the file containing the data. Can be a full path using
        ``r"C:\Temp\inputfile.txt"``, for example.
        The input file must have 3 columns "`\theta` `z` `imp`" expressed
        in Cartesian coordinates.

        This input can also be a ``np.ndarray`` object, with
        `\theta`, `z`, `imp` in each corresponding column.
    m0 : int
        Number of terms along the meridian (`z`).
    n0 : int
        Number of terms along the circumference (`\theta`).
    funcnum : int, optional
        As explained above, selects the base functions used for
        the approximation.
    fem_meridian_bot2top : bool, optional
        A boolean indicating if the finite element has the `x` axis starting
        at the bottom or at the top.
    rotatedeg : float or None, optional
        Rotation angle in degrees telling how much the imperfection pattern
        should be rotated about the `X_3` (or `Z`) axis.
    filter_m0 : list, optional
        The values of ``m0`` that should be filtered (see :func:`.filter_c0`).
    filter_n0 : list, optional
        The values of ``n0`` that should be filtered (see :func:`.filter_c0`).
    sample_size : int or None, optional
        An in  specifying how many points of the imperfection file should
        be used. If ``None`` is used all points file will be used in the
        computations.
    maxmem : int, optional
        Maximum RAM memory in GB allowed to compute the base functions.
        The ``scipy.interpolate.lstsq`` will go beyond this limit.

    Returns
    -------
    out : np.ndarray
        A 1-D array with the best-fit coefficients.

    Notes
    -----
    If a similar imperfection pattern is expected along the meridian and along
    the circumference, the analyst can use an optimized relation between
    ``m0`` and ``n0`` in order to achieve a higher accuracy for a given
    computational cost, as proposed by Castro et al. (2014):

    .. math::
        n_0 = m_0 \frac{\pi(R_{bot}+R_{top})}{2H}

    """
    from scipy.linalg import lstsq

    if isinstance(path, np.ndarray):
        input_pts = path
        path = 'unmamed.txt'
    else:
        input_pts = np.loadtxt(path)

    if input_pts.shape[1] != 3:
        raise ValueError('Input does not have the format: "theta, z, imp"')
    if (input_pts[:,0].min() < -2*np.pi or input_pts[:,0].max() > 2*np.pi):
        raise ValueError(
                'In the input: "theta, z, imp"; "theta" must be in radians!')

    log('Finding c0 coefficients for {0}'.format(str(os.path.basename(path))))
    log('using funcnum {0}'.format(funcnum), level=1)

    if sample_size:
        num = input_pts.shape[0]
        if sample_size < num:
            input_pts = input_pts[sample(range(num), int(sample_size))]

    if funcnum==1:
        size = 2
    elif funcnum==2:
        size = 2
    elif funcnum==3:
        size = 4
    else:
        raise ValueError('Valid values for "funcnum" are 1, 2 or 3')

    # the least-squares algorithm uses approximately the double the memory
    # used by the coefficients matrix. This is non-linear though.
    memfac = 2.2

    maxnum = int(maxmem*1024*1024*1024*8/(64.*size*m0*n0)/memfac)
    num = input_pts.shape[0]
    if num >= maxnum:
        input_pts = input_pts[sample(range(num), int(maxnum))]
        warn('Using {0} measured points due to the "maxmem" specified'.
                format(maxnum), level=1)

    ts = input_pts[:, 0].copy()
    if rotatedeg is not None:
        ts += deg2rad(rotatedeg)
    zs = input_pts[:, 1]
    w0pts = input_pts[:, 2]
    #NOTE using `H_measured` did not allow a good fitting result
    #zs /= H_measured
    zs = (zs - zs.min())/(zs.max() - zs.min())
    if not fem_meridian_bot2top:
        #TODO
        zs *= -1
        zs += 1

    a = fa(m0, n0, zs, ts, funcnum)

    log('Base functions calculated', level=1)
    c0, residues, rank, s = lstsq(a, w0pts)
    log('Finished scipy.linalg.lstsq', level=1)

    if filter_m0 is not None or filter_n0 is not None:
        c0 = filter_c0(m0, n0, c0, filter_m0, filter_n0, funcnum=funcnum)

    return c0, residues
Ejemplo n.º 4
0
                  [-cos(b)*sin(g),
                   (cos(a)*cos(g) - sin(a)*sin(b)*sin(g)),
                   (sin(a)*cos(g) + cos(a)*sin(b)*sin(g)), y0],
                  [sin(b), -sin(a)*cos(b),  cos(a)*cos(b), z0]])


if __name__=='__main__':
    import matplotlib.pyplot as plt

    from _fit_data import fa

    path = r'C:\clones\desicos\desicos\conecylDB\files\dlr\degenhardt_2010_z25\degenhardt_2010_z25_msi_theta_z_imp.txt'
    m0 = 20
    n0 = 20
    c0, residues = calc_c0(path, m0=m0, n0=n0, sample_size=0.75)
    theta = np.linspace(-pi, pi, 1000)
    z = np.linspace(0, 1., 400)

    theta, z = np.meshgrid(theta, z, copy=False)
    a = fa(m0, n0, z.ravel(), theta.ravel(), funcnum=1)
    w = a.dot(c0).reshape(1000, 400)

    levels = np.linspace(w.min(), w.max(), 400)
    plt.contourf(theta, z, w.reshape(theta.shape), levels=levels)

    plt.gcf().savefig('plot.png', transparent=True,
                      bbox_inches='tight', pad_inches=0.05)