Source code for at.matching.globalfit

"""
Collection of functions to fit various global parameters
such as the tune and the chromaticity
"""
from typing import Optional
import numpy
from ..lattice import Lattice, Refpts
from ..lattice import get_value_refpts, set_value_refpts
from ..lattice import AtWarning, AtError
from ..physics import get_tune, get_chrom


__all__ = ['fit_tune', 'fit_chrom']


def _set_magnets(ring, refpts, attname, delta, index=None,
                 increment=True, regex=False, scaling=False):
    if scaling:
        val0 = get_value_refpts(ring, refpts, attname, index=index,
                                regex=regex)
        set_value_refpts(ring, refpts, attname, val0*(1+delta/numpy.mean(val0)),
                         index=index, regex=regex)                        
    else:
        set_value_refpts(ring, refpts, attname, delta, index=index,
                         increment=True, regex=regex)
                 


def _get_tune(ring: Lattice, dp: float, **kwargs):
    get_integer = kwargs.pop('fit_integer', False)
    return get_tune(ring, dp=dp, get_integer=get_integer)[0:2]


def _get_chrom(ring: Lattice, dp: float, **kwargs):
    return get_chrom(ring, dp=dp)[0:2]


def _fit_tune_chrom(ring: Lattice, index: int, func,
                    refpts1: Refpts, refpts2: Refpts, newval,
                    tol: Optional[float] = 1.0e-12,
                    dp: Optional[float] = 0, niter: Optional[int] = 3,
                    delta: Optional[float] = None,
                    regex=False, scaling=False, **kwargs):

    def _get_resp(ring: Lattice, index: int, func, refpts, attname,
                  delta, dp, regex=False, scaling=False, **kwargs):
        _set_magnets(ring, refpts, attname, delta, index=index,
                     increment=True, regex=regex, scaling=scaling)
        datap = func(ring, dp, **kwargs)
        _set_magnets(ring, refpts, attname, -2*delta, index=index,
                     increment=True, regex=regex, scaling=scaling)
        datan = func(ring, dp, **kwargs)
        _set_magnets(ring, refpts, attname, delta, index=index,
                     increment=True, regex=regex, scaling=scaling)
        data = numpy.subtract(datap, datan)/(2*delta)
        return data

    def _fit(ring, index, func, refpts1, refpts2, newval, J,
             dp: Optional[float] = 0, regex=False, **kwargs):
        val = func(ring, dp, **kwargs)
        dk = numpy.linalg.solve(J, numpy.subtract(newval, val))
        _set_magnets(ring, refpts1, 'PolynomB', dk[0], index=index,
                     increment=True, regex=regex, scaling=scaling)
        _set_magnets(ring, refpts2, 'PolynomB', dk[1], index=index,
                     increment=True, regex=regex, scaling=scaling)
        val = func(ring, dp, **kwargs)
        sumsq = numpy.sum(numpy.square(numpy.subtract(val, newval)))
        return sumsq

    if delta is None:
        delta = 1.e-6 * 10 ** index
        
    dq1 = _get_resp(ring, index, func, refpts1, 'PolynomB',
                    delta, dp, regex=regex, **kwargs)
    dq2 = _get_resp(ring, index, func, refpts2, 'PolynomB',
                    delta, dp, regex=regex, **kwargs)
    J = [[dq1[0], dq2[0]], [dq1[1], dq2[1]]]

    n = 0
    sumsq = tol+1
    print('Initial value', func(ring, dp, **kwargs))
    while sumsq > tol and n < niter:
        sumsq = _fit(ring, index, func, refpts1, refpts2, newval,
                     J, dp=dp, regex=regex, **kwargs)
        print('iter#', n, 'Res.', sumsq)
        n += 1
    print('Final value', func(ring, dp, **kwargs), '\n')
    return


[docs] def fit_tune(ring: Lattice, refpts1: Refpts, refpts2: Refpts, newval, tol: float = 1.0e-12, dp: Optional[float] = 0, niter: int = 3, regex=False, KStep: Optional[float] = None, **kwargs) -> None: """Fits the tunes using 2 families Args: ring: Lattice description refpts1: Selection of the 1st family refpts2: Selection of the 2nd family newval: New tunes, in case an non-zero integer part is provided, fit_integer is set to True Keyword arguments: tol: Tolerance for the matching; Default: 1.0e-12 dp: Momentum deviation. Default: 0 niter: Maximum number of iterations. Default 3 fit_integer: bool (default=False), use integer tune regex: Using regular expressions for refpt string matching; Default: False KStep: gradient variation applied to magnets. Default 1e-5 scaling: Scales the families instead of incrementing. Default False Typical usage: at.fit_tune(ring, refpts1, refpts2, [0.1,0.25]) """ print('\nFitting Tune...') if numpy.any(numpy.floor(newval) != 0.0): kwargs['fit_integer'] = True _fit_tune_chrom(ring, 1, _get_tune, refpts1, refpts2, newval, tol=tol, dp=dp, niter=niter, regex=regex, delta=KStep, **kwargs)
[docs] def fit_chrom(ring: Lattice, refpts1: Refpts, refpts2: Refpts, newval, tol: Optional[float] = 1.0e-12, dp: Optional[float] = 0, niter: Optional[int] = 3, regex=False, HStep: Optional[float] = None, **kwargs) -> None: """Fit the chromaticities using 2 families Args: ring: Lattice description refpts1: Selection of the 1st family refpts2: Selection of the 2nd family newval: New tunes Keyword arguments: tol: Tolerance for the matching; Default: 1.0e-12 dp: Momentum deviation. Default: 0 niter: Maximum number of iterations. Default 3 regex: Using regular expressions for refpt string matching; Default: False HStep: gradient variation applied to magnets. Default 1e-4 scaling: Scales the families instead of incrementing them. Default False Typical usage: at.fit_chrom(ring, refpts1, refpts2, [10,5]) """ print('\nFitting Chromaticity...') _fit_tune_chrom(ring, 2, _get_chrom, refpts1, refpts2, newval, tol=tol, dp=dp, niter=niter, regex=regex, delta=HStep, **kwargs)