예제 #1
0
    def _assemble_cpmg_data(self, spin_id=None):
        """Assemble the CPMG data.

        @keyword spin_id:   The spin ID string to restrict data to.
        @type spin_id:      str
        """

        # Spin loop.
        for spin, mol_name, res_num, res_name, id in spin_loop(full_info=True, selection=spin_id, return_id=True, skip_desel=True):
            # The residue index.
            res_index = res_num - 1

            # Sanity checks.
            if res_index < 0:
                raise RelaxError("A residue number of less than 1 is not supported in NESSY.")
            elif res_index > 699:
                raise RelaxError("A residue number of greater than 700 is not supported in NESSY.")

            # Loop over all spectrometer frequencies.
            for exp_type, frq, offset, ei, mi, oi in loop_exp_frq_offset(return_indices=True):
                # Loop over all dispersion points.
                di_new = 0
                for point, di in loop_point(exp_type=exp_type, frq=frq, offset=offset, skip_ref=False, return_indices=True):
                    # The keys.
                    keys = find_intensity_keys(exp_type=exp_type, frq=frq, point=point, time=cdp.relax_time_list[0])

                    # Convert the reference point for NESSY input.
                    if point == None or isNaN(point):
                        point = 0

                    # Loop over the keys.
                    for key in keys:
                        # Another check.
                        if self.cpmg_data[mi][di_new][res_index] != '':
                            raise RelaxError("Only one spin system per residue is supported in NESSY.")

                        # Store the data (if it exists).
                        if key in spin.intensities:
                            self.cpmg_data[mi][di_new][res_index] = str(spin.intensities[key])

                        # The CPMG frequency.
                        self.cpmg_frqs[mi][di_new] = str(point)

                        # Increment the field index.
                        di_new += 1
예제 #2
0
def r2eff_ns_mmq_2site_mq(M0=None, F_vector=array([1, 0], float64), R20A=None, R20B=None, pA=None, dw=None, dwH=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for MQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    and:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             numpy float array of rank [NS][NM][NO][ND]
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             numpy float array of rank [NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NS][NM][NO][ND]
    @keyword dwH:           The proton chemical exchange difference between states A and B in rad/s.
    @type dwH:              numpy float array of rank [NS][NM][NO][ND]
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NS][NM][NO][ND]
    """

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract shape of experiment.
    NS, NM, NO = num_points.shape

    # Populate the m1 and m2 matrices (only once per function call for speed).
    # D+ matrix component.
    m1_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=-dw - dwH, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
    # Z- matrix component.
    m2_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=dw - dwH, k_AB=k_AB, k_BA=k_BA, tcp=tcp)

    # The M1 and M2 matrices.
    # Equivalent to D+.
    M1_mat = matrix_exponential(m1_mat, dtype=complex128)
    # Equivalent to Z-.
    M2_mat = matrix_exponential(m2_mat, dtype=complex128)

    # The complex conjugates M1* and M2*
    # Equivalent to D+*.
    M1_star_mat = conj(M1_mat)
    # Equivalent to Z-*.
    M2_star_mat = conj(M2_mat)

    # Repetitive dot products (minimised for speed).
    M1_M2_mat = einsum('...ij, ...jk', M1_mat, M2_mat)
    M2_M1_mat = einsum('...ij, ...jk', M2_mat, M1_mat)
    M1_M2_M2_M1_mat = einsum('...ij, ...jk', M1_M2_mat, M2_M1_mat)
    M2_M1_M1_M2_mat = einsum('...ij, ...jk', M2_M1_mat, M1_M2_mat)
    M1_M2_star_mat = einsum('...ij, ...jk', M1_star_mat, M2_star_mat)
    M2_M1_star_mat = einsum('...ij, ...jk', M2_star_mat, M1_star_mat)
    M1_M2_M2_M1_star_mat = einsum('...ij, ...jk', M1_M2_star_mat, M2_M1_star_mat)
    M2_M1_M1_M2_star_mat = einsum('...ij, ...jk', M2_M1_star_mat, M1_M2_star_mat)

    # Loop over spins.
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Loop over offsets:
            for oi in range(NO):
                num_points_i = num_points[si, mi, oi]

                # Loop over the time points, back calculating the R2eff values.
                for i in range(num_points_i):
                    # Extract data from array.
                    power_i = int(power[si, mi, oi, i])
                    M1_M2_i = M1_M2_mat[si, mi, oi, i]
                    M1_M2_star_i = M1_M2_star_mat[si, mi, oi, i]
                    M2_M1_i = M2_M1_mat[si, mi, oi, i]
                    M2_M1_star_i = M2_M1_star_mat[si, mi, oi, i]
                    M1_M2_M2_M1_i = M1_M2_M2_M1_mat[si, mi, oi, i]
                    M2_M1_M1_M2_star_i = M2_M1_M1_M2_star_mat[si, mi, oi, i]
                    M2_M1_M1_M2_i = M2_M1_M1_M2_mat[si, mi, oi, i]
                    M1_M2_M2_M1_star_i = M1_M2_M2_M1_star_mat[si, mi, oi, i]

                    # Special case of 1 CPMG block - the power is zero.
                    if power_i == 1:
                        # M1.M2.
                        A = M1_M2_i

                        # M1*.M2*.
                        B = M1_M2_star_i

                        # M2.M1.
                        C = M2_M1_i

                        # M2*.M1*.
                        D = M2_M1_star_i

                    # Matrices for even number of CPMG blocks.
                    elif power_i % 2 == 0:
                        # The power factor (only calculate once).
                        fact = int(floor(power_i / 2))

                        # (M1.M2.M2.M1)^(n/2).
                        A = matrix_power(M1_M2_M2_M1_i, fact)

                        # (M2*.M1*.M1*.M2*)^(n/2).
                        B = matrix_power(M2_M1_M1_M2_star_i, fact)

                        # (M2.M1.M1.M2)^(n/2).
                        C = matrix_power(M2_M1_M1_M2_i, fact)

                        # (M1*.M2*.M2*.M1*)^(n/2).
                        D = matrix_power(M1_M2_M2_M1_star_i, fact)

                    # Matrices for odd number of CPMG blocks.
                    else:
                        # The power factor (only calculate once).
                        fact = int(floor((power_i - 1) / 2))

                        # (M1.M2.M2.M1)^((n-1)/2).M1.M2.
                        A = matrix_power(M1_M2_M2_M1_i, fact)
                        A = dot(A, M1_M2_i)

                        # (M1*.M2*.M2*.M1*)^((n-1)/2).M1*.M2*.
                        B = matrix_power(M1_M2_M2_M1_star_i, fact)
                        B = dot(B, M1_M2_star_i)

                        # (M2.M1.M1.M2)^((n-1)/2).M2.M1.
                        C = matrix_power(M2_M1_M1_M2_i, fact)
                        C = dot(C, M2_M1_i)

                        # (M2*.M1*.M1*.M2*)^((n-1)/2).M2*.M1*.
                        D = matrix_power(M2_M1_M1_M2_star_i, fact)
                        D = dot(D, M2_M1_star_i)

                    # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                    A_B = dot(A, B)
                    C_D = dot(C, D)
                    Mx = dot(dot(F_vector, (A_B + C_D)), M0)
                    Mx = Mx.real / 2.0
                    if Mx <= 0.0 or isNaN(Mx):
                        back_calc[si, mi, oi, i] = 1e99
                    else:
                        back_calc[si, mi, oi, i]= -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
예제 #3
0
def ns_r1rho_2site(M0=None, matrix=None, r1rho_prime=None, omega=None, offset=None, r1=0.0, pA=None, pB=None, dw=None, k_AB=None, k_BA=None, spin_lock_fields=None, relax_time=None, inv_relax_time=None, back_calc=None, num_points=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for R1rho data.

    This function calculates and stores the R1rho values.


    @keyword M0:                This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:                   numpy float64, rank-1, 7D array
    @keyword matrix:            A numpy array to be populated to create the evolution matrix.
    @type matrix:               numpy rank-2, 6D float64 array
    @keyword r1rho_prime:       The R1rho_prime parameter value (R1rho with no exchange).
    @type r1rho_prime:          float
    @keyword omega:             The chemical shift for the spin in rad/s.
    @type omega:                float
    @keyword offset:            The spin-lock offsets for the data.
    @type offset:               numpy rank-1 float array
    @keyword r1:                The R1 relaxation rate.
    @type r1:                   float
    @keyword pA:                The population of state A.
    @type pA:                   float
    @keyword pB:                The population of state B.
    @type pB:                   float
    @keyword dw:                The chemical exchange difference between states A and B in rad/s.
    @type dw:                   float
    @keyword k_AB:              The rate of exchange from site A to B (rad/s).
    @type k_AB:                 float
    @keyword k_BA:              The rate of exchange from site B to A (rad/s).
    @type k_BA:                 float
    @keyword spin_lock_fields:  The R1rho spin-lock field strengths (in rad.s^-1).
    @type spin_lock_fields:     numpy rank-1 float array
    @keyword relax_time:        The total relaxation time period for each spin-lock field strength (in seconds).
    @type relax_time:           float
    @keyword inv_relax_time:    The inverse of the relaxation time period for each spin-lock field strength (in inverse seconds).  This is used for faster calculations.
    @type inv_relax_time:       float
    @keyword back_calc:         The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:            numpy rank-1 float array
    @keyword num_points:        The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:           int
    """

    # Repetitive calculations (to speed up calculations).
    Wa = omega                  # Larmor frequency [s^-1].
    Wb = omega + dw             # Larmor frequency [s^-1].
    W = pA*Wa + pB*Wb           # Population-averaged Larmor frequency [s^-1].
    dA = Wa - offset            # Offset of spin-lock from A.
    dB = Wb - offset            # Offset of spin-lock from B.
    d = W - offset              # Offset of spin-lock from population-average.

    # Loop over the time points, back calculating the R2eff values.
    for i in range(num_points):
        # The matrix that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
        rr1rho_3d(matrix=matrix, R1=r1, r1rho_prime=r1rho_prime, pA=pA, pB=pB, wA=dA, wB=dB, w1=spin_lock_fields[i], k_AB=k_AB, k_BA=k_BA)

        # The following lines rotate the magnetization previous to spin-lock into the weff frame.
        theta = atan(spin_lock_fields[i]/dA)
        M0[0] = sin(theta)    # The A state initial X magnetisation.
        M0[2] = cos(theta)    # The A state initial Z magnetisation.

        # This matrix is a propagator that will evolve the magnetization with the matrix R.
        Rexpo = matrix_exponential(matrix*relax_time)

        # Magnetization evolution.
        MA = dot(M0, dot(Rexpo, M0))

        # The next lines calculate the R1rho using a two-point approximation, i.e. assuming that the decay is mono-exponential.
        if MA <= 0.0 or isNaN(MA):
            back_calc[i] = 1e99
        else:
            back_calc[i]= -inv_relax_time * log(MA)
예제 #4
0
파일: nessy.py 프로젝트: tlinnet/relax
    def _assemble_cpmg_data(self, spin_id=None):
        """Assemble the CPMG data.

        @keyword spin_id:   The spin ID string to restrict data to.
        @type spin_id:      str
        """

        # Spin loop.
        for spin, mol_name, res_num, res_name, id in spin_loop(
                full_info=True, selection=spin_id, return_id=True,
                skip_desel=True):
            # The residue index.
            res_index = res_num - 1

            # Sanity checks.
            if res_index < 0:
                raise RelaxError(
                    "A residue number of less than 1 is not supported in NESSY."
                )
            elif res_index > 699:
                raise RelaxError(
                    "A residue number of greater than 700 is not supported in NESSY."
                )

            # Loop over all spectrometer frequencies.
            for exp_type, frq, offset, ei, mi, oi in loop_exp_frq_offset(
                    return_indices=True):
                # Loop over all dispersion points.
                di_new = 0
                for point, di in loop_point(exp_type=exp_type,
                                            frq=frq,
                                            offset=offset,
                                            skip_ref=False,
                                            return_indices=True):
                    # The keys.
                    keys = find_intensity_keys(exp_type=exp_type,
                                               frq=frq,
                                               point=point,
                                               time=cdp.relax_time_list[0])

                    # Convert the reference point for NESSY input.
                    if point == None or isNaN(point):
                        point = 0

                    # Loop over the keys.
                    for key in keys:
                        # Another check.
                        if self.cpmg_data[mi][di_new][res_index] != '':
                            raise RelaxError(
                                "Only one spin system per residue is supported in NESSY."
                            )

                        # Store the data (if it exists).
                        if key in spin.peak_intensity:
                            self.cpmg_data[mi][di_new][res_index] = str(
                                spin.peak_intensity[key])

                        # The CPMG frequency.
                        self.cpmg_frqs[mi][di_new] = str(point)

                        # Increment the field index.
                        di_new += 1
예제 #5
0
def r2eff_ns_cpmg_2site_star(M0=None, r20a=None, r20b=None, pA=None, dw=None, dw_orig=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation using complex conjugate matrices.

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 2D array
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword dw_orig:       The chemical exchange difference between states A and B in ppm. This is only for faster checking of zero value, which result in no exchange.
    @type dw_orig:          numpy float array of rank-1
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NE][NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NE][NS][NM][NO][ND]
    """

    # Flag to tell if values should be replaced if math function is violated.
    t_dw_zero = False

    # Catch parameter values that will result in no exchange, returning flat R2eff = R20 lines (when kex = 0.0, k_AB = 0.0).
    if pA == 1.0 or kex == 0.0:
        back_calc[:] = r20a
        return

    # Test if dw is zero. Create a mask for the affected spins to replace these with R20 at the end of the calculationWait for replacement, since this is spin specific.
    if min(fabs(dw_orig)) == 0.0:
        t_dw_zero = True
        mask_dw_zero = masked_where(dw == 0.0, dw)

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract the total numbers of experiments, number of spins, number of magnetic field strength, number of offsets, maximum number of dispersion point.
    NE, NS, NM, NO, ND = back_calc.shape

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R_mat, cR2_mat, Rr_mat, Rex_mat, RCS_mat = rcpmg_star_rankN(R2A=r20a, R2B=r20b, dw=dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)

    # The the essential evolution matrix.
    # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
    eR_mat = matrix_exponential(R_mat)
    ecR2_mat = matrix_exponential(cR2_mat)

    # Preform the matrix.
    # This is the propagator for an element of [delay tcp; 180 deg pulse; 2 times delay tcp; 180 deg pulse; delay tau], i.e. for 2 times tau-180-tau.
    prop_2_mat = evolution_matrix_mat = einsum('...ij, ...jk', eR_mat, ecR2_mat)
    prop_2_mat = evolution_matrix_mat = einsum('...ij, ...jk', prop_2_mat, eR_mat)

    # Loop over the spins
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Extract the values from the higher dimensional arrays.
            num_points_si_mi = int(num_points[0, si, mi, 0])

            # Loop over the time points, back calculating the R2eff values.
            for di in range(num_points_si_mi):
                # Extract the values from the higher dimensional arrays.
                power_si_mi_di = int(power[0, si, mi, 0, di])

                # This is the propagator for an element of [delay tcp; 180 deg pulse; 2 times delay tcp; 180 deg pulse; delay tau], i.e. for 2 times tau-180-tau.
                prop_2_i = prop_2_mat[0, si, mi, 0, di]

                # Now create the total propagator that will evolve the magnetization under the CPMG train, i.e. it applies the above tau-180-tau-tau-180-tau so many times as required for the CPMG frequency under consideration.
                prop_total = matrix_power(prop_2_i, power_si_mi_di)

                # Now we apply the above propagator to the initial magnetization vector - resulting in the magnetization that remains after the full CPMG pulse train.  It is called M of t (t is the time after the CPMG train).
                Moft = dot(prop_total, M0)

                # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                Mx = Moft[0].real / M0[0]
                if Mx <= 0.0 or isNaN(Mx):
                    back_calc[0, si, mi, 0, di] = 1e99
                else:
                    back_calc[0, si, mi, 0, di]= -inv_tcpmg[0, si, mi, 0, di] * log(Mx)

    # Replace data in array.
    # If dw is zero.
    if t_dw_zero:
        back_calc[mask_dw_zero.mask] = r20a[mask_dw_zero.mask]

    # Catch errors, taking a sum over array is the fastest way to check for
    # +/- inf (infinity) and nan (not a number).
    if not isfinite(sum(back_calc)):
        # Replaces nan, inf, etc. with fill value.
        fix_invalid(back_calc, copy=False, fill_value=1e100)
예제 #6
0
def r2eff_ns_mmq_2site_mq(M0=None,
                          F_vector=array([1, 0], float64),
                          R20A=None,
                          R20B=None,
                          pA=None,
                          dw=None,
                          dwH=None,
                          kex=None,
                          inv_tcpmg=None,
                          tcp=None,
                          back_calc=None,
                          num_points=None,
                          power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for MQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    and:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             numpy float array of rank [NS][NM][NO][ND]
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             numpy float array of rank [NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NS][NM][NO][ND]
    @keyword dwH:           The proton chemical exchange difference between states A and B in rad/s.
    @type dwH:              numpy float array of rank [NS][NM][NO][ND]
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NS][NM][NO][ND]
    """

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract shape of experiment.
    NS, NM, NO = num_points.shape

    # Populate the m1 and m2 matrices (only once per function call for speed).
    # D+ matrix component.
    m1_mat = rmmq_2site_rankN(R20A=R20A,
                              R20B=R20B,
                              dw=-dw - dwH,
                              k_AB=k_AB,
                              k_BA=k_BA,
                              tcp=tcp)
    # Z- matrix component.
    m2_mat = rmmq_2site_rankN(R20A=R20A,
                              R20B=R20B,
                              dw=dw - dwH,
                              k_AB=k_AB,
                              k_BA=k_BA,
                              tcp=tcp)

    # The M1 and M2 matrices.
    # Equivalent to D+.
    M1_mat = matrix_exponential(m1_mat, dtype=complex128)
    # Equivalent to Z-.
    M2_mat = matrix_exponential(m2_mat, dtype=complex128)

    # The complex conjugates M1* and M2*
    # Equivalent to D+*.
    M1_star_mat = conj(M1_mat)
    # Equivalent to Z-*.
    M2_star_mat = conj(M2_mat)

    # Repetitive dot products (minimised for speed).
    M1_M2_mat = einsum('...ij, ...jk', M1_mat, M2_mat)
    M2_M1_mat = einsum('...ij, ...jk', M2_mat, M1_mat)
    M1_M2_M2_M1_mat = einsum('...ij, ...jk', M1_M2_mat, M2_M1_mat)
    M2_M1_M1_M2_mat = einsum('...ij, ...jk', M2_M1_mat, M1_M2_mat)
    M1_M2_star_mat = einsum('...ij, ...jk', M1_star_mat, M2_star_mat)
    M2_M1_star_mat = einsum('...ij, ...jk', M2_star_mat, M1_star_mat)
    M1_M2_M2_M1_star_mat = einsum('...ij, ...jk', M1_M2_star_mat,
                                  M2_M1_star_mat)
    M2_M1_M1_M2_star_mat = einsum('...ij, ...jk', M2_M1_star_mat,
                                  M1_M2_star_mat)

    # Loop over spins.
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Loop over offsets:
            for oi in range(NO):
                num_points_i = num_points[si, mi, oi]

                # Loop over the time points, back calculating the R2eff values.
                for i in range(num_points_i):
                    # Extract data from array.
                    power_i = int(power[si, mi, oi, i])
                    M1_M2_i = M1_M2_mat[si, mi, oi, i]
                    M1_M2_star_i = M1_M2_star_mat[si, mi, oi, i]
                    M2_M1_i = M2_M1_mat[si, mi, oi, i]
                    M2_M1_star_i = M2_M1_star_mat[si, mi, oi, i]
                    M1_M2_M2_M1_i = M1_M2_M2_M1_mat[si, mi, oi, i]
                    M2_M1_M1_M2_star_i = M2_M1_M1_M2_star_mat[si, mi, oi, i]
                    M2_M1_M1_M2_i = M2_M1_M1_M2_mat[si, mi, oi, i]
                    M1_M2_M2_M1_star_i = M1_M2_M2_M1_star_mat[si, mi, oi, i]

                    # Special case of 1 CPMG block - the power is zero.
                    if power_i == 1:
                        # M1.M2.
                        A = M1_M2_i

                        # M1*.M2*.
                        B = M1_M2_star_i

                        # M2.M1.
                        C = M2_M1_i

                        # M2*.M1*.
                        D = M2_M1_star_i

                    # Matrices for even number of CPMG blocks.
                    elif power_i % 2 == 0:
                        # The power factor (only calculate once).
                        fact = int(floor(power_i / 2))

                        # (M1.M2.M2.M1)^(n/2).
                        A = matrix_power(M1_M2_M2_M1_i, fact)

                        # (M2*.M1*.M1*.M2*)^(n/2).
                        B = matrix_power(M2_M1_M1_M2_star_i, fact)

                        # (M2.M1.M1.M2)^(n/2).
                        C = matrix_power(M2_M1_M1_M2_i, fact)

                        # (M1*.M2*.M2*.M1*)^(n/2).
                        D = matrix_power(M1_M2_M2_M1_star_i, fact)

                    # Matrices for odd number of CPMG blocks.
                    else:
                        # The power factor (only calculate once).
                        fact = int(floor((power_i - 1) / 2))

                        # (M1.M2.M2.M1)^((n-1)/2).M1.M2.
                        A = matrix_power(M1_M2_M2_M1_i, fact)
                        A = dot(A, M1_M2_i)

                        # (M1*.M2*.M2*.M1*)^((n-1)/2).M1*.M2*.
                        B = matrix_power(M1_M2_M2_M1_star_i, fact)
                        B = dot(B, M1_M2_star_i)

                        # (M2.M1.M1.M2)^((n-1)/2).M2.M1.
                        C = matrix_power(M2_M1_M1_M2_i, fact)
                        C = dot(C, M2_M1_i)

                        # (M2*.M1*.M1*.M2*)^((n-1)/2).M2*.M1*.
                        D = matrix_power(M2_M1_M1_M2_star_i, fact)
                        D = dot(D, M2_M1_star_i)

                    # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                    A_B = dot(A, B)
                    C_D = dot(C, D)
                    Mx = dot(dot(F_vector, (A_B + C_D)), M0)
                    Mx = Mx.real / 2.0
                    if Mx <= 0.0 or isNaN(Mx):
                        back_calc[si, mi, oi, i] = 1e99
                    else:
                        back_calc[si, mi, oi,
                                  i] = -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
예제 #7
0
def r2eff_ns_mmq_2site_mq(M0=None, F_vector=array([1, 0], float64), m1=None, m2=None, R20A=None, R20B=None, pA=None, pB=None, dw=None, dwH=None, k_AB=None, k_BA=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for MQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    and:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword m1:            A complex numpy matrix to be populated.
    @type m1:               numpy rank-2, 2D complex64 array
    @keyword m2:            A complex numpy matrix to be populated.
    @type m2:               numpy rank-2, 2D complex64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             float
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             float
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword pB:            The population of state B.
    @type pB:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               float
    @keyword dwH:           The proton chemical exchange difference between states A and B in rad/s.
    @type dwH:              float
    @keyword k_AB:          The rate of exchange from site A to B (rad/s).
    @type k_AB:             float
    @keyword k_BA:          The rate of exchange from site B to A (rad/s).
    @type k_BA:             float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        float
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy rank-1 float array
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy rank-1 float array
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       int
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int16, rank-1 array
    """

    # Populate the m1 and m2 matrices (only once per function call for speed).
    populate_matrix(matrix=m1, R20A=R20A, R20B=R20B, dw=-dw-dwH, k_AB=k_AB, k_BA=k_BA)     # D+ matrix component.
    populate_matrix(matrix=m2, R20A=R20A, R20B=R20B, dw=dw-dwH, k_AB=k_AB, k_BA=k_BA)    # Z- matrix component.

    # Loop over the time points, back calculating the R2eff values.
    for i in range(num_points):
        # The M1 and M2 matrices.
        M1 = matrix_exponential(m1*tcp[i])    # Equivalent to D+.
        M2 = matrix_exponential(m2*tcp[i])    # Equivalent to Z-.

        # The complex conjugates M1* and M2*
        M1_star = conj(M1)    # Equivalent to D+*.
        M2_star = conj(M2)    # Equivalent to Z-*.

        # Repetitive dot products (minimised for speed).
        M1_M2 = dot(M1, M2)
        M2_M1 = dot(M2, M1)
        M1_M2_M2_M1 = dot(M1_M2, M2_M1)
        M2_M1_M1_M2 = dot(M2_M1, M1_M2)
        M1_M2_star = dot(M1_star, M2_star)
        M2_M1_star = dot(M2_star, M1_star)
        M1_M2_M2_M1_star = dot(M1_M2_star, M2_M1_star)
        M2_M1_M1_M2_star = dot(M2_M1_star, M1_M2_star)

        # Special case of 1 CPMG block - the power is zero.
        if power[i] == 1:
            # M1.M2.
            A = M1_M2

            # M1*.M2*.
            B = M1_M2_star

            # M2.M1.
            C = M2_M1

            # M2*.M1*.
            D = M2_M1_star

        # Matrices for even number of CPMG blocks.
        elif power[i] % 2 == 0:
            # The power factor (only calculate once).
            fact = int(floor(power[i] / 2))

            # (M1.M2.M2.M1)^(n/2).
            A = square_matrix_power(M1_M2_M2_M1, fact)

            # (M2*.M1*.M1*.M2*)^(n/2).
            B = square_matrix_power(M2_M1_M1_M2_star, fact)

            # (M2.M1.M1.M2)^(n/2).
            C = square_matrix_power(M2_M1_M1_M2, fact)

            # (M1*.M2*.M2*.M1*)^(n/2).
            D = square_matrix_power(M1_M2_M2_M1_star, fact)

        # Matrices for odd number of CPMG blocks.
        else:
            # The power factor (only calculate once).
            fact = int(floor((power[i] - 1) / 2))

            # (M1.M2.M2.M1)^((n-1)/2).M1.M2.
            A = square_matrix_power(M1_M2_M2_M1, fact)
            A = dot(A, M1_M2)

            # (M1*.M2*.M2*.M1*)^((n-1)/2).M1*.M2*.
            B = square_matrix_power(M1_M2_M2_M1_star, fact)
            B = dot(B, M1_M2_star)

            # (M2.M1.M1.M2)^((n-1)/2).M2.M1.
            C = square_matrix_power(M2_M1_M1_M2, fact)
            C = dot(C, M2_M1)

            # (M2*.M1*.M1*.M2*)^((n-1)/2).M2*.M1*.
            D = square_matrix_power(M2_M1_M1_M2_star, fact)
            D = dot(D, M2_M1_star)

        # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
        A_B = dot(A, B)
        C_D = dot(C, D)
        Mx = dot(dot(F_vector, (A_B + C_D)), M0)
        Mx = Mx.real / 2.0
        if Mx <= 0.0 or isNaN(Mx):
            back_calc[i] = 1e99
        else:
            back_calc[i]= -inv_tcpmg * log(Mx / pA)
예제 #8
0
def disassemble_result(
    param_vector=None,
    func=None,
    iter=None,
    fc=None,
    gc=None,
    hc=None,
    warning=None,
    spin=None,
    sim_index=None,
    model_type=None,
    scaling_matrix=None,
):
    """Disassemble the optimisation results.

    @keyword param_vector:      The model-free parameter vector.
    @type param_vector:         numpy array
    @keyword func:              The optimised chi-squared value.
    @type func:                 float
    @keyword iter:              The number of optimisation steps required to find the minimum.
    @type iter:                 int
    @keyword fc:                The function count.
    @type fc:                   int
    @keyword gc:                The gradient count.
    @type gc:                   int
    @keyword hc:                The Hessian count.
    @type hc:                   int
    @keyword warning:           Any optimisation warnings.
    @type warning:              str or None
    @keyword spin:              The spin container.
    @type spin:                 SpinContainer instance or None
    @keyword sim_index:         The Monte Carlo simulation index.
    @type sim_index:            int or None
    @keyword model_type:        The model-free model type, one of 'mf', 'local_tm', 'diff', or
                                'all'.
    @type model_type:           str
    @keyword scaling_matrix:    The diagonal, square scaling matrix.
    @type scaling_matrix:       numpy diagonal matrix
    """

    # No result.
    if param_vector is None:
        return

    # Alias the current data pipe.
    cdp = pipes.get_pipe()

    # Catch infinite chi-squared values.
    if isInf(func):
        raise RelaxInfError("chi-squared")

    # Catch chi-squared values of NaN.
    if isNaN(func):
        raise RelaxNaNError("chi-squared")

    # Scaling.
    if scaling_matrix is not None:
        param_vector = dot(scaling_matrix, param_vector)

    # Check if the chi-squared value is lower.  This allows for a parallelised grid search!
    if sim_index == None:
        # Get the correct value.
        chi2 = None
        if (model_type == "mf" or model_type == "local_tm") and hasattr(cdp, "chi2"):
            chi2 = spin.chi2
        if (model_type == "diff" or model_type == "all") and hasattr(cdp, "chi2"):
            chi2 = cdp.chi2

        # Spin text.
        spin_text = ""
        if spin != None and hasattr(spin, "_spin_ids") and len(spin._spin_ids):
            spin_text = " for the spin '%s'" % spin._spin_ids[0]

        # No improvement.
        if chi2 != None and func >= chi2:
            print(
                "Discarding the optimisation results%s, the optimised chi-squared value is higher than the current value (%s >= %s)."
                % (spin_text, func, chi2)
            )

            # Exit!
            return

        # New minimum.
        else:
            print(
                "Storing the optimisation results%s, the optimised chi-squared value is lower than the current value (%s < %s)."
                % (spin_text, func, chi2)
            )

    # Disassemble the parameter vector.
    disassemble_param_vector(model_type, param_vector=param_vector, spin=spin, sim_index=sim_index)

    # Monte Carlo minimisation statistics.
    if sim_index != None:
        # Sequence specific minimisation statistics.
        if model_type == "mf" or model_type == "local_tm":

            # Chi-squared statistic.
            spin.chi2_sim[sim_index] = func

            # Iterations.
            spin.iter_sim[sim_index] = iter

            # Function evaluations.
            spin.f_count_sim[sim_index] = fc

            # Gradient evaluations.
            spin.g_count_sim[sim_index] = gc

            # Hessian evaluations.
            spin.h_count_sim[sim_index] = hc

            # Warning.
            spin.warning_sim[sim_index] = warning

        # Global minimisation statistics.
        elif model_type == "diff" or model_type == "all":
            # Chi-squared statistic.
            cdp.chi2_sim[sim_index] = func

            # Iterations.
            cdp.iter_sim[sim_index] = iter

            # Function evaluations.
            cdp.f_count_sim[sim_index] = fc

            # Gradient evaluations.
            cdp.g_count_sim[sim_index] = gc

            # Hessian evaluations.
            cdp.h_count_sim[sim_index] = hc

            # Warning.
            cdp.warning_sim[sim_index] = warning

    # Normal statistics.
    else:
        # Sequence specific minimisation statistics.
        if model_type == "mf" or model_type == "local_tm":
            # Chi-squared statistic.
            spin.chi2 = func

            # Iterations.
            spin.iter = iter

            # Function evaluations.
            spin.f_count = fc

            # Gradient evaluations.
            spin.g_count = gc

            # Hessian evaluations.
            spin.h_count = hc

            # Warning.
            spin.warning = warning

        # Global minimisation statistics.
        elif model_type == "diff" or model_type == "all":
            # Chi-squared statistic.
            cdp.chi2 = func

            # Iterations.
            cdp.iter = iter

            # Function evaluations.
            cdp.f_count = fc

            # Gradient evaluations.
            cdp.g_count = gc

            # Hessian evaluations.
            cdp.h_count = hc

            # Warning.
            cdp.warning = warning
예제 #9
0
def r2eff_ns_cpmg_2site_3D(r180x=None, M0=None, M0_T=None, r10a=0.0, r10b=0.0, r20a=None, r20b=None, pA=None, dw=None, dw_orig=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation.

    This function calculates and stores the R2eff values.


    @keyword r180x:         The X-axis pi-pulse propagator.
    @type r180x:            numpy float64, rank-2, 7D array
    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float array of rank [NE][NS][NM][NO][ND][7][1]
    @keyword M0_T:          This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations, where the outer two axis has been swapped for efficient dot operations.
    @type M0_T:             numpy float array of rank [NE][NS][NM][NO][ND][1][7]
    @keyword r10a:          The R1 value for state A.
    @type r10a:             float
    @keyword r10b:          The R1 value for state B.
    @type r10b:             float
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword dw_orig:       The chemical exchange difference between states A and B in ppm. This is only for faster checking of zero value, which result in no exchange.
    @type dw_orig:          numpy float array of rank-1
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NE][NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NE][NS][NM][NO][ND]
    """

    # Flag to tell if values should be replaced if math function is violated.
    t_dw_zero = False

    # Catch parameter values that will result in no exchange, returning flat R2eff = R20 lines (when kex = 0.0, k_AB = 0.0).
    if pA == 1.0 or kex == 0.0:
        back_calc[:] = r20a
        return

    # Test if dw is zero. Create a mask for the affected spins to replace these with R20 at the end of the calculationWait for replacement, since this is spin specific.
    if min(fabs(dw_orig)) == 0.0:
        t_dw_zero = True
        mask_dw_zero = masked_where(dw == 0.0, dw)

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0_T[:, :, :, :, :, 0, 1] = pA
    M0_T[:, :, :, :, :, 0, 4] = pB
    M0[:, :, :, :, :, 1, 0] = pA
    M0[:, :, :, :, :, 4, 0] = pB

    # Extract the total numbers of experiments, number of spins, number of magnetic field strength, number of offsets, maximum number of dispersion point.
    NE, NS, NM, NO, ND = back_calc.shape

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R_mat = rcpmg_3d_rankN(R1A=r10a, R1B=r10b, R2A=r20a, R2B=r20b, pA=pA, pB=pB, dw=dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)

    # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
    Rexpo_mat = matrix_exponential(R_mat)

    # The the essential evolution matrix.
    # This is a dot product of the outer [7][7] matrix of the Rexpo_mat and r180x matrixes, which
    # have the shape [NE][NS][NM][NO][ND][7][7] and [7][7].
    # This can be achieved by using numpy einsum, and where ellipsis notation will use the last axis.
    evolution_matrix_mat = einsum('...ij,...jk', Rexpo_mat, r180x)
    evolution_matrix_mat = einsum('...ij,...jk', evolution_matrix_mat, Rexpo_mat)
    evolution_matrix_mat = einsum('...ij,...jk', evolution_matrix_mat, evolution_matrix_mat)

    # Roll axis around.
    evolution_matrix_T_mat = rollaxis(evolution_matrix_mat, 6, 5)

    # Preform the initial magnetisation.
    evolution_matrix_T_M0_mat = einsum('...ij,...jk', M0_T, evolution_matrix_T_mat)

    # Loop over the spins
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Extract number of points.
            num_points_si_mi = int(num_points[0, si, mi, 0])

            # Loop over the time points, back calculating the R2eff values.
            for di in range(num_points_si_mi):
                # Extract the values from the higher dimensional arrays.
                inv_tcpmg_si_mi_di = inv_tcpmg[0, si, mi, 0, di]
                power_si_mi_di = int(power[0, si, mi, 0, di])
                r20a_si_mi_di = r20a[0, si, mi, 0, di]

                # Initial magnetisation.
                Mint_T_i = evolution_matrix_T_M0_mat[0, si, mi, 0, di]

                # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
                evolution_matrix_T_i = evolution_matrix_T_mat[0, si, mi, 0, di]

                # Get which power to raise the matrix to.
                l = int(power_si_mi_di-1)

                # Raise the square evolution matrix to the power l.
                evolution_matrix_T_power_i = matrix_power(evolution_matrix_T_i, l)

                # Evolve the magnetisation.
                Mint_T_i = dot(Mint_T_i, evolution_matrix_T_power_i)

                # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                Mx = Mint_T_i[0][1] / pA
                if Mx <= 0.0 or isNaN(Mx):
                    back_calc[0, si, mi, 0, di] = r20a_si_mi_di
                else:
                    back_calc[0, si, mi, 0, di] = - inv_tcpmg_si_mi_di * log(Mx)

    # Replace data in array.
    # If dw is zero.
    if t_dw_zero:
        back_calc[mask_dw_zero.mask] = r20a[mask_dw_zero.mask]

    # Catch errors, taking a sum over array is the fastest way to check for
    # +/- inf (infinity) and nan (not a number).
    if not isfinite(sum(back_calc)):
        # Replaces nan, inf, etc. with fill value.
        fix_invalid(back_calc, copy=False, fill_value=1e100)
예제 #10
0
def r2eff_ns_cpmg_2site_star(Rr=None, Rex=None, RCS=None, R=None, M0=None, r20a=None, r20b=None, dw=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation using complex conjugate matrices.

    This function calculates and stores the R2eff values.


    @keyword Rr:            The matrix that contains only the R2 relaxation terms ("Redfield relaxation", i.e. non-exchange broadening).
    @type Rr:               numpy complex64, rank-2, 2D array
    @keyword Rex:           The matrix that contains the exchange terms between the two states A and B.
    @type Rex:              numpy complex64, rank-2, 2D array
    @keyword RCS:           The matrix that contains the chemical shift evolution.  It works here only with X magnetization, and the complex notation allows to evolve in the transverse plane (x, y).
    @type RCS:              numpy complex64, rank-2, 2D array
    @keyword R:             The matrix that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    @type R:                numpy complex64, rank-2, 2D array
    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 2D array
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             float
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        float
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy rank-1 float array
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy rank-1 float array
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       int
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int16, rank-1 array
    """

    # The matrix that contains only the R2 relaxation terms ("Redfield relaxation", i.e. non-exchange broadening).
    Rr[0, 0] = -r20a
    Rr[1, 1] = -r20b

    # The matrix that contains the chemical shift evolution.  It works here only with X magnetization, and the complex notation allows to evolve in the transverse plane (x, y).  The chemical shift for state A is assumed to be zero.
    RCS[1, 1] = complex(0.0, -dw)

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R = add(Rr, Rex)
    R = add(R, RCS)

    # This is the complex conjugate of the above.  It allows the chemical shift to run in the other direction, i.e. it is used to evolve the shift after a 180 deg pulse.  The factor of 2 is to minimise the number of multiplications for the prop_2 matrix calculation.
    cR2 = conj(R) * 2.0

    # Loop over the time points, back calculating the R2eff values.
    for i in range(num_points):
        # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
        eR_tcp = matrix_exponential(R*tcp[i])

        # This is the propagator for an element of [delay tcp; 180 deg pulse; 2 times delay tcp; 180 deg pulse; delay tau], i.e. for 2 times tau-180-tau.
        prop_2 = dot(dot(eR_tcp, matrix_exponential(cR2*tcp[i])), eR_tcp)

        # Now create the total propagator that will evolve the magnetization under the CPMG train, i.e. it applies the above tau-180-tau-tau-180-tau so many times as required for the CPMG frequency under consideration.
        prop_total = square_matrix_power(prop_2, power[i])

        # Now we apply the above propagator to the initial magnetization vector - resulting in the magnetization that remains after the full CPMG pulse train.  It is called M of t (t is the time after the CPMG train).
        Moft = dot(prop_total, M0)

        # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
        Mx = Moft[0].real / M0[0]
        if Mx <= 0.0 or isNaN(Mx):
            back_calc[i] = 1e99
        else:
            back_calc[i]= -inv_tcpmg * log(Mx)
예제 #11
0
def r2eff_ns_cpmg_2site_3D(r180x=None, M0=None, r10a=0.0, r10b=0.0, r20a=None, r20b=None, pA=None, pB=None, dw=None, k_AB=None, k_BA=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation.

    This function calculates and stores the R2eff values.


    @keyword r180x:         The X-axis pi-pulse propagator.
    @type r180x:            numpy float64, rank-2, 7D array
    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword r10a:          The R1 value for state A.
    @type r10a:             float
    @keyword r10b:          The R1 value for state B.
    @type r10b:             float
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             float
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             float
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword pB:            The population of state B.
    @type pB:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               float
    @keyword k_AB:          The rate of exchange from site A to B (rad/s).
    @type k_AB:             float
    @keyword k_BA:          The rate of exchange from site B to A (rad/s).
    @type k_BA:             float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        float
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy rank-1 float array
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy rank-1 float array
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       int
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int16, rank-1 array
    """

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R = rcpmg_3d(R1A=r10a, R1B=r10b, R2A=r20a, R2B=r20b, pA=pA, pB=pB, dw=dw, k_AB=k_AB, k_BA=k_BA)

    # Loop over the time points, back calculating the R2eff values.
    for i in range(num_points):
        # Initial magnetisation.
        Mint = M0

        # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
        Rexpo = matrix_exponential(R*tcp[i])

        # Loop over the CPMG elements, propagating the magnetisation.
        for j in range(2*power[i]):
            Mint = dot(Rexpo, Mint)
            Mint = dot(r180x, Mint)
            Mint = dot(Rexpo, Mint)

        # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
        Mx = fabs(Mint[1] / pA)
        if Mx <= 0.0 or isNaN(Mx):
            back_calc[i] = 1e99
        else:
            back_calc[i]= -inv_tcpmg * log(Mx)
예제 #12
0
파일: api.py 프로젝트: pombredanne/relax
    def minimise(self, min_algor=None, min_options=None, func_tol=None, grad_tol=None, max_iterations=None, constraints=False, scaling_matrix=None, verbosity=0, sim_index=None, lower=None, upper=None, inc=None):
        """Minimisation function.

        @param min_algor:           The minimisation algorithm to use.
        @type min_algor:            str
        @param min_options:         An array of options to be used by the minimisation algorithm.
        @type min_options:          array of str
        @param func_tol:            The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
        @type func_tol:             None or float
        @param grad_tol:            The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
        @type grad_tol:             None or float
        @param max_iterations:      The maximum number of iterations for the algorithm.
        @type max_iterations:       int
        @param constraints:         If True, constraints are used during optimisation.
        @type constraints:          bool
        @keyword scaling_matrix:    The per-model list of diagonal and square scaling matrices.
        @type scaling_matrix:       list of numpy rank-2, float64 array or list of None
        @param verbosity:           A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @param sim_index:           The index of the simulation to optimise.  This should be None if normal optimisation is desired.
        @type sim_index:            None or int
        @keyword lower:             The per-model lower bounds of the grid search which must be equal to the number of parameters in the model.  This optional argument is only used when doing a grid search.
        @type lower:                list of lists of numbers
        @keyword upper:             The per-model upper bounds of the grid search which must be equal to the number of parameters in the model.  This optional argument is only used when doing a grid search.
        @type upper:                list of lists of numbers
        @keyword inc:               The per-model increments for each dimension of the space for the grid search.  The number of elements in the array must equal to the number of parameters in the model.  This argument is only used when doing a grid search.
        @type inc:                  list of lists of int
        """

        # Set up the target function for direct calculation.
        model, param_vector, data_types = target_fn_setup(sim_index=sim_index, scaling_matrix=scaling_matrix[0], verbosity=verbosity)

        # Nothing to do!
        if not len(param_vector):
            warn(RelaxWarning("The model has no parameters, minimisation cannot be performed."))
            return

        # Right, constraints cannot be used for the 'fixed' model.
        if constraints and cdp.model == 'fixed':
            if verbosity:
                warn(RelaxWarning("Turning constraints off.  These cannot be used for the 'fixed' model."))
            constraints = False

            # Pop out the Method of Multipliers algorithm.
            if min_algor == 'Method of Multipliers':
                min_algor = min_options[0]
                min_options = min_options[1:]

        # And constraints absolutely must be used for the 'population' model.
        if not constraints and cdp.model == 'population':
            warn(RelaxWarning("Turning constraints on.  These absolutely must be used for the 'population' model."))
            constraints = True

            # Add the Method of Multipliers algorithm.
            min_options = (min_algor,) + min_options
            min_algor = 'Method of Multipliers'

        # Disallow Newton optimisation and other Hessian optimisers for the paramagnetic centre position optimisation (the PCS Hessian is not yet implemented).
        if hasattr(cdp, 'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
            if min_algor in ['newton']:
                raise RelaxError("For the paramagnetic centre position, as the Hessians are not yet implemented Newton optimisation cannot be performed.")

        # Linear constraints.
        A, b = None, None
        if constraints:
            A, b = linear_constraints(data_types=data_types, scaling_matrix=scaling_matrix[0])

        # Grid search.
        if search('^[Gg]rid', min_algor):
            # The search.
            results = grid(func=model.func, args=(), num_incs=inc[0], lower=lower[0], upper=upper[0], A=A, b=b, verbosity=verbosity)

            # Unpack the results.
            param_vector, func, iter_count, warning = results
            f_count = iter_count
            g_count = 0.0
            h_count = 0.0

        # Minimisation.
        else:
            results = generic_minimise(func=model.func, dfunc=model.dfunc, d2func=model.d2func, args=(), x0=param_vector, min_algor=min_algor, min_options=min_options, func_tol=func_tol, grad_tol=grad_tol, maxiter=max_iterations, A=A, b=b, full_output=1, print_flag=verbosity)

            # Unpack the results.
            if results == None:
                return
            param_vector, func, iter_count, f_count, g_count, h_count, warning = results

        # Catch infinite chi-squared values.
        if isInf(func):
            raise RelaxInfError('chi-squared')

        # Catch chi-squared values of NaN.
        if isNaN(func):
            raise RelaxNaNError('chi-squared')

        # Make a last function call to update the back-calculated RDC and PCS structures to the optimal values.
        chi2 = model.func(param_vector)

        # Scaling.
        if scaling_matrix[0] is not None:
            param_vector = dot(scaling_matrix[0], param_vector)

        # Disassemble the parameter vector.
        disassemble_param_vector(param_vector=param_vector, data_types=data_types, sim_index=sim_index)

        # Monte Carlo minimisation statistics.
        if sim_index != None:
            # Chi-squared statistic.
            cdp.chi2_sim[sim_index] = func

            # Iterations.
            cdp.iter_sim[sim_index] = iter_count

            # Function evaluations.
            cdp.f_count_sim[sim_index] = f_count

            # Gradient evaluations.
            cdp.g_count_sim[sim_index] = g_count

            # Hessian evaluations.
            cdp.h_count_sim[sim_index] = h_count

            # Warning.
            cdp.warning_sim[sim_index] = warning

        # Normal statistics.
        else:
            # Chi-squared statistic.
            cdp.chi2 = func

            # Iterations.
            cdp.iter = iter_count

            # Function evaluations.
            cdp.f_count = f_count

            # Gradient evaluations.
            cdp.g_count = g_count

            # Hessian evaluations.
            cdp.h_count = h_count

            # Warning.
            cdp.warning = warning

        # Statistical analysis.
        if 'rdc' in data_types or 'pcs' in data_types:
            # Get the final back calculated data (for the Q factor and
            minimise_bc_data(model, sim_index=sim_index)

            # Calculate the RDC Q factors.
            if 'rdc' in data_types:
                rdc.q_factors(sim_index=sim_index, verbosity=verbosity)

            # Calculate the PCS Q factors.
            if 'pcs' in data_types:
                pcs.q_factors(sim_index=sim_index, verbosity=verbosity)
예제 #13
0
파일: api.py 프로젝트: tlinnet/relax
    def minimise(self,
                 min_algor=None,
                 min_options=None,
                 func_tol=None,
                 grad_tol=None,
                 max_iterations=None,
                 constraints=False,
                 scaling_matrix=None,
                 verbosity=0,
                 sim_index=None,
                 lower=None,
                 upper=None,
                 inc=None):
        """Minimisation function.

        @param min_algor:           The minimisation algorithm to use.
        @type min_algor:            str
        @param min_options:         An array of options to be used by the minimisation algorithm.
        @type min_options:          array of str
        @param func_tol:            The function tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
        @type func_tol:             None or float
        @param grad_tol:            The gradient tolerance which, when reached, terminates optimisation. Setting this to None turns of the check.
        @type grad_tol:             None or float
        @param max_iterations:      The maximum number of iterations for the algorithm.
        @type max_iterations:       int
        @param constraints:         If True, constraints are used during optimisation.
        @type constraints:          bool
        @keyword scaling_matrix:    The per-model list of diagonal and square scaling matrices.
        @type scaling_matrix:       list of numpy rank-2, float64 array or list of None
        @param verbosity:           A flag specifying the amount of information to print.  The higher the value, the greater the verbosity.
        @type verbosity:            int
        @param sim_index:           The index of the simulation to optimise.  This should be None if normal optimisation is desired.
        @type sim_index:            None or int
        @keyword lower:             The per-model lower bounds of the grid search which must be equal to the number of parameters in the model.  This optional argument is only used when doing a grid search.
        @type lower:                list of lists of numbers
        @keyword upper:             The per-model upper bounds of the grid search which must be equal to the number of parameters in the model.  This optional argument is only used when doing a grid search.
        @type upper:                list of lists of numbers
        @keyword inc:               The per-model increments for each dimension of the space for the grid search.  The number of elements in the array must equal to the number of parameters in the model.  This argument is only used when doing a grid search.
        @type inc:                  list of lists of int
        """

        # Set up the target function for direct calculation.
        model, param_vector, data_types = target_fn_setup(
            sim_index=sim_index,
            scaling_matrix=scaling_matrix[0],
            verbosity=verbosity)

        # Nothing to do!
        if not len(param_vector):
            warn(
                RelaxWarning(
                    "The model has no parameters, minimisation cannot be performed."
                ))
            return

        # Right, constraints cannot be used for the 'fixed' model.
        if constraints and cdp.model == 'fixed':
            if verbosity:
                warn(
                    RelaxWarning(
                        "Turning constraints off.  These cannot be used for the 'fixed' model."
                    ))
            constraints = False

            # Pop out the Method of Multipliers algorithm.
            if min_algor == 'Method of Multipliers':
                min_algor = min_options[0]
                min_options = min_options[1:]

        # And constraints absolutely must be used for the 'population' model.
        if not constraints and cdp.model == 'population':
            warn(
                RelaxWarning(
                    "Turning constraints on.  These absolutely must be used for the 'population' model."
                ))
            constraints = True

            # Add the Method of Multipliers algorithm.
            min_options = (min_algor, ) + min_options
            min_algor = 'Method of Multipliers'

        # Disallow Newton optimisation and other Hessian optimisers for the paramagnetic centre position optimisation (the PCS Hessian is not yet implemented).
        if hasattr(cdp,
                   'paramag_centre_fixed') and not cdp.paramag_centre_fixed:
            if min_algor in ['newton']:
                raise RelaxError(
                    "For the paramagnetic centre position, as the Hessians are not yet implemented Newton optimisation cannot be performed."
                )

        # Linear constraints.
        A, b = None, None
        if constraints:
            A, b = linear_constraints(data_types=data_types,
                                      scaling_matrix=scaling_matrix[0])

        # Grid search.
        if search('^[Gg]rid', min_algor):
            # The search.
            results = grid(func=model.func,
                           args=(),
                           num_incs=inc[0],
                           lower=lower[0],
                           upper=upper[0],
                           A=A,
                           b=b,
                           verbosity=verbosity)

            # Unpack the results.
            param_vector, func, iter_count, warning = results
            f_count = iter_count
            g_count = 0.0
            h_count = 0.0

        # Minimisation.
        else:
            results = generic_minimise(func=model.func,
                                       dfunc=model.dfunc,
                                       d2func=model.d2func,
                                       args=(),
                                       x0=param_vector,
                                       min_algor=min_algor,
                                       min_options=min_options,
                                       func_tol=func_tol,
                                       grad_tol=grad_tol,
                                       maxiter=max_iterations,
                                       A=A,
                                       b=b,
                                       full_output=1,
                                       print_flag=verbosity)

            # Unpack the results.
            if results == None:
                return
            param_vector, func, iter_count, f_count, g_count, h_count, warning = results

        # Catch infinite chi-squared values.
        if isInf(func):
            raise RelaxInfError('chi-squared')

        # Catch chi-squared values of NaN.
        if isNaN(func):
            raise RelaxNaNError('chi-squared')

        # Make a last function call to update the back-calculated RDC and PCS structures to the optimal values.
        chi2 = model.func(param_vector)

        # Scaling.
        if scaling_matrix[0] is not None:
            param_vector = dot(scaling_matrix[0], param_vector)

        # Disassemble the parameter vector.
        disassemble_param_vector(param_vector=param_vector,
                                 data_types=data_types,
                                 sim_index=sim_index)

        # Monte Carlo minimisation statistics.
        if sim_index != None:
            # Chi-squared statistic.
            cdp.chi2_sim[sim_index] = func

            # Iterations.
            cdp.iter_sim[sim_index] = iter_count

            # Function evaluations.
            cdp.f_count_sim[sim_index] = f_count

            # Gradient evaluations.
            cdp.g_count_sim[sim_index] = g_count

            # Hessian evaluations.
            cdp.h_count_sim[sim_index] = h_count

            # Warning.
            cdp.warning_sim[sim_index] = warning

        # Normal statistics.
        else:
            # Chi-squared statistic.
            cdp.chi2 = func

            # Iterations.
            cdp.iter = iter_count

            # Function evaluations.
            cdp.f_count = f_count

            # Gradient evaluations.
            cdp.g_count = g_count

            # Hessian evaluations.
            cdp.h_count = h_count

            # Warning.
            cdp.warning = warning

        # Statistical analysis.
        if 'rdc' in data_types or 'pcs' in data_types:
            # Get the final back calculated data (for the Q factor and
            minimise_bc_data(model, sim_index=sim_index)

            # Calculate the RDC Q factors.
            if 'rdc' in data_types:
                rdc.q_factors(sim_index=sim_index, verbosity=verbosity)

            # Calculate the PCS Q factors.
            if 'pcs' in data_types:
                pcs.q_factors(sim_index=sim_index, verbosity=verbosity)
예제 #14
0
def r2eff_ns_cpmg_2site_3D(r180x=None,
                           M0=None,
                           M0_T=None,
                           r10a=0.0,
                           r10b=0.0,
                           r20a=None,
                           r20b=None,
                           pA=None,
                           dw=None,
                           dw_orig=None,
                           kex=None,
                           inv_tcpmg=None,
                           tcp=None,
                           back_calc=None,
                           num_points=None,
                           power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation.

    This function calculates and stores the R2eff values.


    @keyword r180x:         The X-axis pi-pulse propagator.
    @type r180x:            numpy float64, rank-2, 7D array
    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float array of rank [NE][NS][NM][NO][ND][7][1]
    @keyword M0_T:          This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations, where the outer two axis has been swapped for efficient dot operations.
    @type M0_T:             numpy float array of rank [NE][NS][NM][NO][ND][1][7]
    @keyword r10a:          The R1 value for state A.
    @type r10a:             float
    @keyword r10b:          The R1 value for state B.
    @type r10b:             float
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword dw_orig:       The chemical exchange difference between states A and B in ppm. This is only for faster checking of zero value, which result in no exchange.
    @type dw_orig:          numpy float array of rank-1
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NE][NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NE][NS][NM][NO][ND]
    """

    # Flag to tell if values should be replaced if math function is violated.
    t_dw_zero = False

    # Catch parameter values that will result in no exchange, returning flat R2eff = R20 lines (when kex = 0.0, k_AB = 0.0).
    if pA == 1.0 or kex == 0.0:
        back_calc[:] = r20a
        return

    # Test if dw is zero. Create a mask for the affected spins to replace these with R20 at the end of the calculationWait for replacement, since this is spin specific.
    if min(fabs(dw_orig)) == 0.0:
        t_dw_zero = True
        mask_dw_zero = masked_where(dw == 0.0, dw)

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0_T[:, :, :, :, :, 0, 1] = pA
    M0_T[:, :, :, :, :, 0, 4] = pB
    M0[:, :, :, :, :, 1, 0] = pA
    M0[:, :, :, :, :, 4, 0] = pB

    # Extract the total numbers of experiments, number of spins, number of magnetic field strength, number of offsets, maximum number of dispersion point.
    NE, NS, NM, NO, ND = back_calc.shape

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R_mat = rcpmg_3d_rankN(R1A=r10a,
                           R1B=r10b,
                           R2A=r20a,
                           R2B=r20b,
                           pA=pA,
                           pB=pB,
                           dw=dw,
                           k_AB=k_AB,
                           k_BA=k_BA,
                           tcp=tcp)

    # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
    Rexpo_mat = matrix_exponential(R_mat)

    # The the essential evolution matrix.
    # This is a dot product of the outer [7][7] matrix of the Rexpo_mat and r180x matrixes, which
    # have the shape [NE][NS][NM][NO][ND][7][7] and [7][7].
    # This can be achieved by using numpy einsum, and where ellipsis notation will use the last axis.
    evolution_matrix_mat = einsum('...ij,...jk', Rexpo_mat, r180x)
    evolution_matrix_mat = einsum('...ij,...jk', evolution_matrix_mat,
                                  Rexpo_mat)
    evolution_matrix_mat = einsum('...ij,...jk', evolution_matrix_mat,
                                  evolution_matrix_mat)

    # Roll axis around.
    evolution_matrix_T_mat = rollaxis(evolution_matrix_mat, 6, 5)

    # Preform the initial magnetisation.
    evolution_matrix_T_M0_mat = einsum('...ij,...jk', M0_T,
                                       evolution_matrix_T_mat)

    # Loop over the spins
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Extract number of points.
            num_points_si_mi = int(num_points[0, si, mi, 0])

            # Loop over the time points, back calculating the R2eff values.
            for di in range(num_points_si_mi):
                # Extract the values from the higher dimensional arrays.
                inv_tcpmg_si_mi_di = inv_tcpmg[0, si, mi, 0, di]
                power_si_mi_di = int(power[0, si, mi, 0, di])
                r20a_si_mi_di = r20a[0, si, mi, 0, di]

                # Initial magnetisation.
                Mint_T_i = evolution_matrix_T_M0_mat[0, si, mi, 0, di]

                # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
                evolution_matrix_T_i = evolution_matrix_T_mat[0, si, mi, 0, di]

                # Get which power to raise the matrix to.
                l = int(power_si_mi_di - 1)

                # Raise the square evolution matrix to the power l.
                evolution_matrix_T_power_i = matrix_power(
                    evolution_matrix_T_i, l)

                # Evolve the magnetisation.
                Mint_T_i = dot(Mint_T_i, evolution_matrix_T_power_i)

                # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                Mx = Mint_T_i[0][1] / pA
                if Mx <= 0.0 or isNaN(Mx):
                    back_calc[0, si, mi, 0, di] = r20a_si_mi_di
                else:
                    back_calc[0, si, mi, 0, di] = -inv_tcpmg_si_mi_di * log(Mx)

    # Replace data in array.
    # If dw is zero.
    if t_dw_zero:
        back_calc[mask_dw_zero.mask] = r20a[mask_dw_zero.mask]

    # Catch errors, taking a sum over array is the fastest way to check for
    # +/- inf (infinity) and nan (not a number).
    if not isfinite(sum(back_calc)):
        # Replaces nan, inf, etc. with fill value.
        fix_invalid(back_calc, copy=False, fill_value=1e100)
예제 #15
0
def r2eff_ns_mmq_2site_sq_dq_zq(M0=None, F_vector=array([1, 0], float64), R20A=None, R20B=None, pA=None, dw=None, dwH=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for SQ, ZQ, and DQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             numpy float array of rank [NS][NM][NO][ND]
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             numpy float array of rank [NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The combined chemical exchange difference between states A and B in rad/s.  It should be set to dwH for 1H SQ data, dw for heteronuclear SQ data, dwH-dw for ZQ data, and dwH+dw for DQ data.
    @type dw:               numpy float array of rank [NS][NM][NO][ND]
    @keyword dwH:           Unused - this is simply to match the r2eff_ns_mmq_2site_mq() function arguments.
    @type dwH:              numpy float array of rank [NS][NM][NO][ND]
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NS][NM][NO][ND]
    """

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract shape of experiment.
    NS, NM, NO = num_points.shape

    # Populate the m1 and m2 matrices (only once per function call for speed).
    m1_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)
    m2_mat = rmmq_2site_rankN(R20A=R20A, R20B=R20B, dw=-dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)

    # The A+/- matrices.
    A_pos_mat = matrix_exponential(m1_mat, dtype=complex128)
    A_neg_mat = matrix_exponential(m2_mat, dtype=complex128)

    # The evolution for one n.
    evol_block_mat = einsum('...ij, ...jk', A_neg_mat, A_pos_mat)
    evol_block_mat = einsum('...ij, ...jk', A_neg_mat, evol_block_mat)
    evol_block_mat = einsum('...ij, ...jk', A_pos_mat, evol_block_mat)

    # Loop over spins.
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Loop over offsets:
            for oi in range(NO):
                # Extract number of points.
                num_points_i = num_points[si, mi, oi]

                # Loop over the time points, back calculating the R2eff values.
                for i in range(num_points_i):
                    # Extract data from array.
                    power_i = int(power[si, mi, oi, i])
                    evol_block_i = evol_block_mat[si, mi, oi, i]

                    # The full evolution.
                    evol = matrix_power(evol_block_i, power_i)

                    # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                    Mx = dot(F_vector, dot(evol, M0))
                    Mx = Mx.real
                    if Mx <= 0.0 or isNaN(Mx):
                        back_calc[si, mi, oi, i] = 1e99
                    else:
                        back_calc[si, mi, oi, i] = -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
def r2eff_ns_cpmg_2site_expanded(r20=None, pA=None, dw=None, k_AB=None, k_BA=None, relax_time=None, inv_relax_time=None, tcp=None, back_calc=None, num_points=None, num_cpmg=None):
    """The 2-site numerical solution to the Bloch-McConnell equation using complex conjugate matrices.

    This function calculates and stores the R2eff values.


    @keyword r20:               The R2 value for both states A and B in the absence of exchange.
    @type r20:                  float
    @keyword pA:                The population of state A.
    @type pA:                   float
    @keyword dw:                The chemical exchange difference between states A and B in rad/s.
    @type dw:                   float
    @keyword k_AB:              The rate of exchange from site A to B (rad/s).
    @type k_AB:                 float
    @keyword k_BA:              The rate of exchange from site B to A (rad/s).
    @type k_BA:                 float
    @keyword relax_time:        The total relaxation time period (in seconds).
    @type relax_time:           float
    @keyword inv_relax_time:    The inverse of the total relaxation time period (in inverse seconds).
    @type inv_relax_time:       float
    @keyword tcp:               The tau_CPMG times (1 / 4.nu1).
    @type tcp:                  numpy rank-1 float array
    @keyword back_calc:         The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:            numpy rank-1 float array
    @keyword num_points:        The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:           int
    @keyword num_cpmg:          The array of numbers of CPMG blocks.
    @type num_cpmg:             numpy int16, rank-1 array
    """

    # Repeditive calculations.
    half_tcp = 0.5 * tcp
    k_AB_plus_k_BA = k_AB + k_BA
    k_BA_minus_k_AB = k_BA - k_AB

    # The expansion factors (in numpy array form for all dispersion points).
    t3 = 1.j
    t4 = t3 * dw
    two_t4 = 2.0 * t4
    t5 = k_BA**2
    t8 = two_t4 * k_BA
    t10 = 2.0 * k_BA * k_AB
    t11 = dw**2
    t14 = two_t4 * k_AB
    t15 = k_AB**2
    t5_t10_t11_t15 = t5 + t10 - t11 + t15
    t8_t14 = t8 - t14
    t17 = sqrt(t5_t10_t11_t15 - t8_t14)

    k_AB_plus_k_BA_minus_t4 = k_AB_plus_k_BA - t4
    t21 = exp((t17 - k_AB_plus_k_BA_minus_t4) * half_tcp)
    t22 = 1.0/t17
    t28 = exp(-(t17 + k_AB_plus_k_BA_minus_t4) * half_tcp)
    t31 = t22*k_AB * (t21 - t28)
    t33 = sqrt(t5_t10_t11_t15 + t8_t14)

    k_AB_plus_k_BA_plus_t4 = k_AB_plus_k_BA + t4
    k_BA_minus_k_AB_plus_t4 = k_BA_minus_k_AB + t4
    t34 = k_BA_minus_k_AB_plus_t4 + t33
    t37 = exp((t33 - k_AB_plus_k_BA_plus_t4) * tcp)
    t39 = 1.0/t33
    t41 = k_BA_minus_k_AB_plus_t4 - t33
    t44 = exp(-(t33 + k_AB_plus_k_BA_plus_t4) * tcp)
    t47 = 0.5*t39 * (t34*t37 - t41*t44)

    k_BA_minus_k_AB_minus_t4 = k_BA_minus_k_AB - t4
    t49 = k_BA_minus_k_AB_minus_t4 - t17
    t51 = t21 * t49 * t22
    t52 = k_BA_minus_k_AB_minus_t4 + t17
    t54 = t28 * t52 * t22
    t55 = -t51 + t54
    t60 = 0.5*t39*k_AB * (t37 - t44)
    t62 = t31*t47 + t55*t60
    t63 = 1.0/k_AB
    t68 = 0.5*t63 * (t49*t54 - t52*t51)
    t69 = 0.5*t62 * t68
    t72 = t37 * t41 * t39
    t76 = t44 * t34 * t39
    t78 = 0.5*t63 * (t41*t76 - t34*t72)
    t80 = 0.5 * (t76 - t72)
    t82 = 0.5 * (t31*t78 + t55*t80)
    t83 = t82 * t55/2.0
    t88 = 0.5 * t22 * (t52*t21 - t49*t28)
    t91 = t88 * t47 + t68*t60
    t92 = t91 * t88
    t95 = 0.5 * (t88*t78 + t68*t80)
    t96 = t95 * t31
    t97 = t69 + t83
    t98 = t97**2
    t99 = t92 + t96
    t102 = t99**2
    t108 = t62 * t88 + t82 * t31
    t112 = sqrt(t98 - 2.0 * t99 * t97 + t102 + 2.0 * (t91 * t68 + t95 * t55) * t108)
    t97_t99 = t97 + t99
    t97_nt99 = t97 - t99
    t113 = t97_nt99 - t112
    t115 = num_cpmg
    t116 = power(0.5*(t97_t99 + t112), t115)
    t118 = 1.0/t112
    t120 = t97_nt99 + t112
    t122 = power(0.5*(t97_t99 - t112), t115)
    t127 = 0.5/t108
    t120_t122 = t120*t122
    t139 = 0.5/(k_AB + k_BA) * ((t120_t122 - t113*t116)*t118*k_BA + (t120_t122 - t116*t120)*t127*t113*t118*k_AB)

    # Calculate the initial and final peak intensities.
    intensity0 = pA
    intensity = t139.real * exp(-relax_time * r20)

    # The magnetisation vector.
    Mx = intensity / intensity0

    # Calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential, and store it for each dispersion point.
    for i in range(num_points):
        if Mx[i] <= 0.0 or isNaN(Mx[i]):
            back_calc[i] = 1e99
        else:
            back_calc[i]= -inv_relax_time * log(Mx[i])
예제 #17
0
def r2eff_ns_cpmg_2site_star(M0=None, r20a=None, r20b=None, pA=None, dw=None, dw_orig=None, kex=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation using complex conjugate matrices.

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 2D array
    @keyword r20a:          The R2 value for state A in the absence of exchange.
    @type r20a:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword r20b:          The R2 value for state B in the absence of exchange.
    @type r20b:             numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The chemical exchange difference between states A and B in rad/s.
    @type dw:               numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword dw_orig:       The chemical exchange difference between states A and B in ppm. This is only for faster checking of zero value, which result in no exchange.
    @type dw_orig:          numpy float array of rank-1
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NE][NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NE][NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NE][NS][NM][NO][ND]
    """

    # Flag to tell if values should be replaced if math function is violated.
    t_dw_zero = False

    # Catch parameter values that will result in no exchange, returning flat R2eff = R20 lines (when kex = 0.0, k_AB = 0.0).
    if pA == 1.0 or kex == 0.0:
        back_calc[:] = r20a
        return

    # Test if dw is zero. Create a mask for the affected spins to replace these with R20 at the end of the calculationWait for replacement, since this is spin specific.
    if min(fabs(dw_orig)) == 0.0:
        t_dw_zero = True
        mask_dw_zero = masked_where(dw == 0.0, dw)

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract the total numbers of experiments, number of spins, number of magnetic field strength, number of offsets, maximum number of dispersion point.
    NE, NS, NM, NO, ND = back_calc.shape

    # The matrix R that contains all the contributions to the evolution, i.e. relaxation, exchange and chemical shift evolution.
    R_mat, cR2_mat, Rr_mat, Rex_mat, RCS_mat = rcpmg_star_rankN(R2A=r20a, R2B=r20b, dw=dw, k_AB=k_AB, k_BA=k_BA, tcp=tcp)

    # The the essential evolution matrix.
    # This matrix is a propagator that will evolve the magnetization with the matrix R for a delay tcp.
    eR_mat = matrix_exponential(R_mat)
    ecR2_mat = matrix_exponential(cR2_mat)

    # Preform the matrix.
    # This is the propagator for an element of [delay tcp; 180 deg pulse; 2 times delay tcp; 180 deg pulse; delay tau], i.e. for 2 times tau-180-tau.
    prop_2_mat = evolution_matrix_mat = einsum('...ij, ...jk', eR_mat, ecR2_mat)
    prop_2_mat = evolution_matrix_mat = einsum('...ij, ...jk', prop_2_mat, eR_mat)

    # Loop over the spins
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Extract the values from the higher dimensional arrays.
            num_points_si_mi = int(num_points[0, si, mi, 0])

            # Loop over the time points, back calculating the R2eff values.
            for di in range(num_points_si_mi):
                # Extract the values from the higher dimensional arrays.
                power_si_mi_di = int(power[0, si, mi, 0, di])

                # This is the propagator for an element of [delay tcp; 180 deg pulse; 2 times delay tcp; 180 deg pulse; delay tau], i.e. for 2 times tau-180-tau.
                prop_2_i = prop_2_mat[0, si, mi, 0, di]

                # Now create the total propagator that will evolve the magnetization under the CPMG train, i.e. it applies the above tau-180-tau-tau-180-tau so many times as required for the CPMG frequency under consideration.
                prop_total = matrix_power(prop_2_i, power_si_mi_di)

                # Now we apply the above propagator to the initial magnetization vector - resulting in the magnetization that remains after the full CPMG pulse train.  It is called M of t (t is the time after the CPMG train).
                Moft = dot(prop_total, M0)

                # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                Mx = Moft[0].real / M0[0]
                if Mx <= 0.0 or isNaN(Mx):
                    back_calc[0, si, mi, 0, di] = 1e99
                else:
                    back_calc[0, si, mi, 0, di]= -inv_tcpmg[0, si, mi, 0, di] * log(Mx)

    # Replace data in array.
    # If dw is zero.
    if t_dw_zero:
        back_calc[mask_dw_zero.mask] = r20a[mask_dw_zero.mask]

    # Catch errors, taking a sum over array is the fastest way to check for
    # +/- inf (infinity) and nan (not a number).
    if not isfinite(sum(back_calc)):
        # Replaces nan, inf, etc. with fill value.
        fix_invalid(back_calc, copy=False, fill_value=1e100)
예제 #18
0
def grid_setup(lower=None, upper=None, inc=None, verbosity=1, skip_preset=True):
    """Determine the per-model grid bounds, allowing for the zooming grid search.

    @keyword lower:         The user supplied lower bounds of the grid search which must be equal to the number of parameters in the model.
    @type lower:            list of numbers
    @keyword upper:         The user supplied upper bounds of the grid search which must be equal to the number of parameters in the model.
    @type upper:            list of numbers
    @keyword inc:           The user supplied grid search increments.
    @type inc:              int or list of int
    @keyword verbosity:     The amount of information to print.  The higher the value, the greater the verbosity.
    @type verbosity:        int
    @keyword skip_preset:   This argument, when True, allows any parameter which already has a value set to be skipped in the grid search.
    @type skip_preset:      bool
    @return:                The per-model grid upper and lower bounds.  The first dimension of each structure corresponds to the model, the second the model parameters.
    @rtype:                 tuple of lists of lists of float, lists of lists of float, list of lists of int
    """

    # The specific analysis API object and parameter object.
    api = return_api()
    param_object = return_parameter_object()

    # Initialise.
    model_lower = []
    model_upper = []
    model_inc = []

    # Loop over the models.
    for model_info in api.model_loop():
        # Get the parameter names and current values.
        names = api.get_param_names(model_info)
        values = api.get_param_values(model_info)

        # No parameters for this model.
        if names == None or len(names) == 0:
            model_lower.append([])
            model_upper.append([])
            model_inc.append([])
            continue

        # The parameter number.
        n = len(names)

        # Make sure that the length of the parameter array is > 0.
        if n == 0:
            raise RelaxError("Cannot run a grid search on a model with zero parameters.")

        # Check that the user supplied bound lengths are ok.
        if lower != None and len(lower) != n:
            raise RelaxLenError('lower bounds', n)
        if upper != None and len(upper) != n:
            raise RelaxLenError('upper bounds', n)

        # Check the user supplied increments.
        if isinstance(inc, list) and len(inc) != n:
            raise RelaxLenError('increment', n)
        if isinstance(inc, list):
            for i in range(n):
                if not (isinstance(inc[i], int) or inc[i] == None):
                    raise RelaxIntListIntError('increment', inc)
        elif not isinstance(inc, int):
            raise RelaxIntListIntError('increment', inc)

        # Convert to the model increment list.
        if isinstance(inc, int):
            model_inc.append([inc]*n)
        else:
            model_inc.append(inc)

        # Print out the model title.
        api.print_model_title(prefix="Grid search setup:  ", model_info=model_info)

        # The grid zoom level.
        zoom = 0
        if hasattr(cdp, 'grid_zoom_level'):
            zoom = cdp.grid_zoom_level
        zoom_factor = 1.0 / 2.0**zoom
        if zoom > 0:
            print("Zooming grid level of %s, scaling the grid size by a factor of %s.\n" % (zoom, zoom_factor))

        # Append empty lists for the bounds to be built up.
        model_lower.append([])
        model_upper.append([])

        # Loop over the parameters.
        data = []
        for i in range(n):
            # A comment for user feedback.
            comment = 'Default bounds'
            if lower != None and upper != None:
                comment = 'User supplied lower and upper bound'
            elif lower != None:
                comment = 'User supplied lower bound'
            elif upper != None:
                comment = 'User supplied upper bound'

            # Alias the number of increments for this parameter.
            incs = model_inc[-1][i]

            # Error checking for increment values of None.
            if incs == None and values[i] in [None, {}, []]:
                raise RelaxError("The parameter '%s' has no preset value, therefore a grid increment of None is not valid." % names[i])

            # The lower bound for this parameter.
            if lower != None:
                lower_i = lower[i]
            else:
                lower_i = param_object.grid_lower(names[i], incs=incs, model_info=model_info)

            # The upper bound for this parameter.
            if upper != None:
                upper_i = upper[i]
            else:
                upper_i = param_object.grid_upper(names[i], incs=incs, model_info=model_info)

            # The skipping logic.
            skip = False
            if skip_preset:
                # Override the flag if the zoom is on.
                if zoom:
                    skip = False

                # No preset value.
                elif values[i] in [None, {}, []]:
                    skip = False

                # The preset value is a NaN value due to numpy conversions of None.
                elif isNaN(values[i]):
                    skip = False

                # Ok, now the parameter can be skipped.
                else:
                    skip = True

            # Override the skip flag if the incs value is None.
            if incs == None:
                skip = True

            # Skip preset values.
            if skip:
                lower_i = values[i]
                upper_i = values[i]
                model_inc[-1][i] = incs = 1
                comment = 'Preset value'

            # Zooming grid.
            elif zoom:
                # The full size and scaled size.
                size = upper_i - lower_i
                zoom_size = size * zoom_factor
                half_size = zoom_size / 2.0
                comment = 'Zoom grid width of %s %s' % (zoom_size, param_object.units(names[i]))

                # The new size around the current value.
                lower_zoom = values[i] - half_size
                upper_zoom = values[i] + half_size

                # Outside of the original lower bound, so shift the grid to fit.
                if zoom > 0 and lower_zoom < lower_i:
                    # The amount to shift by.
                    shift = lower_i - lower_zoom

                    # Set the new bounds.
                    upper_i = upper_zoom + shift

                # Outside of the original upper bound, so shift the grid to fit.
                elif zoom > 0 and upper_zoom > upper_i:
                    # The amount to shift by.
                    shift = upper_i - upper_zoom

                    # Set the new bounds.
                    lower_i = lower_zoom + shift

                # Inside the original bounds.
                else:
                    lower_i = lower_zoom
                    upper_i = upper_zoom

            # Add to the data list for printing out.
            data.append([names[i], "%15s" % lower_i, "%15s" % upper_i, "%15s" % incs, comment])

            # Scale the bounds.
            scaling = param_object.scaling(names[i], model_info=model_info)
            lower_i /= scaling
            upper_i /= scaling

            # Append.
            model_lower[-1].append(lower_i)
            model_upper[-1].append(upper_i)

        # Printout.
        if verbosity:
            write_data(out=sys.stdout, headings=["Parameter", "Lower bound", "Upper bound", "Increments", "Comment"], data=data)
            sys.stdout.write('\n')

    # Return the bounds.
    return model_lower, model_upper, model_inc
예제 #19
0
def r2eff_ns_mmq_2site_sq_dq_zq(M0=None, F_vector=array([1, 0], float64), m1=None, m2=None, R20A=None, R20B=None, pA=None, pB=None, dw=None, dwH=None, k_AB=None, k_BA=None, inv_tcpmg=None, tcp=None, back_calc=None, num_points=None, power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for SQ, ZQ, and DQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword m1:            A complex numpy matrix to be populated.
    @type m1:               numpy rank-2, 2D complex64 array
    @keyword m2:            A complex numpy matrix to be populated.
    @type m2:               numpy rank-2, 2D complex64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             float
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             float
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword pB:            The population of state B.
    @type pB:               float
    @keyword dw:            The combined chemical exchange difference between states A and B in rad/s.  It should be set to dwH for 1H SQ data, dw for heteronuclear SQ data, dwH-dw for ZQ data, and dwH+dw for DQ data.
    @type dw:               float
    @keyword dwH:           Unused - this is simply to match the r2eff_ns_mmq_2site_mq() function arguments.
    @type dwH:              float
    @keyword k_AB:          The rate of exchange from site A to B (rad/s).
    @type k_AB:             float
    @keyword k_BA:          The rate of exchange from site B to A (rad/s).
    @type k_BA:             float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        float
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy rank-1 float array
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy rank-1 float array
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       int
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int16, rank-1 array
    """

    # Populate the m1 and m2 matrices (only once per function call for speed).
    populate_matrix(matrix=m1, R20A=R20A, R20B=R20B, dw=dw, k_AB=k_AB, k_BA=k_BA)
    populate_matrix(matrix=m2, R20A=R20A, R20B=R20B, dw=-dw, k_AB=k_AB, k_BA=k_BA)

    # Loop over the time points, back calculating the R2eff values.
    for i in range(num_points):
        # The A+/- matrices.
        A_pos = matrix_exponential(m1*tcp[i])
        A_neg = matrix_exponential(m2*tcp[i])

        # The evolution for one n.
        evol_block = dot(A_pos, dot(A_neg, dot(A_neg, A_pos)))

        # The full evolution.
        evol = square_matrix_power(evol_block, power[i])

        # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
        Mx = dot(F_vector, dot(evol, M0))
        Mx = Mx.real
        if Mx <= 0.0 or isNaN(Mx):
            back_calc[i] = 1e99
        else:
            back_calc[i] = -inv_tcpmg * log(Mx / pA)
예제 #20
0
def grid_setup(lower=None,
               upper=None,
               inc=None,
               verbosity=1,
               skip_preset=True):
    """Determine the per-model grid bounds, allowing for the zooming grid search.

    @keyword lower:         The user supplied lower bounds of the grid search which must be equal to the number of parameters in the model.
    @type lower:            list of numbers
    @keyword upper:         The user supplied upper bounds of the grid search which must be equal to the number of parameters in the model.
    @type upper:            list of numbers
    @keyword inc:           The user supplied grid search increments.
    @type inc:              int or list of int
    @keyword verbosity:     The amount of information to print.  The higher the value, the greater the verbosity.
    @type verbosity:        int
    @keyword skip_preset:   This argument, when True, allows any parameter which already has a value set to be skipped in the grid search.
    @type skip_preset:      bool
    @return:                The per-model grid upper and lower bounds.  The first dimension of each structure corresponds to the model, the second the model parameters.
    @rtype:                 tuple of lists of lists of float, lists of lists of float, list of lists of int
    """

    # The specific analysis API object and parameter object.
    api = return_api()
    param_object = return_parameter_object()

    # Initialise.
    model_lower = []
    model_upper = []
    model_inc = []

    # Loop over the models.
    for model_info in api.model_loop():
        # Get the parameter names and current values.
        names = api.get_param_names(model_info)
        values = api.get_param_values(model_info)

        # No parameters for this model.
        if names == None or len(names) == 0:
            model_lower.append([])
            model_upper.append([])
            model_inc.append([])
            continue

        # The parameter number.
        n = len(names)

        # Make sure that the length of the parameter array is > 0.
        if n == 0:
            raise RelaxError(
                "Cannot run a grid search on a model with zero parameters.")

        # Check that the user supplied bound lengths are ok.
        if lower != None and len(lower) != n:
            raise RelaxLenError('lower bounds', n)
        if upper != None and len(upper) != n:
            raise RelaxLenError('upper bounds', n)

        # Check the user supplied increments.
        if isinstance(inc, list) and len(inc) != n:
            raise RelaxLenError('increment', n)
        if isinstance(inc, list):
            for i in range(n):
                if not (isinstance(inc[i], int) or inc[i] == None):
                    raise RelaxIntListIntError('increment', inc)
        elif not isinstance(inc, int):
            raise RelaxIntListIntError('increment', inc)

        # Convert to the model increment list.
        if isinstance(inc, int):
            model_inc.append([inc] * n)
        else:
            model_inc.append(inc)

        # Print out the model title.
        api.print_model_title(prefix="Grid search setup:  ",
                              model_info=model_info)

        # The grid zoom level.
        zoom = 0
        if hasattr(cdp, 'grid_zoom_level'):
            zoom = cdp.grid_zoom_level
        zoom_factor = 1.0 / 2.0**zoom
        if zoom > 0:
            print(
                "Zooming grid level of %s, scaling the grid size by a factor of %s.\n"
                % (zoom, zoom_factor))

        # Append empty lists for the bounds to be built up.
        model_lower.append([])
        model_upper.append([])

        # Loop over the parameters.
        data = []
        for i in range(n):
            # A comment for user feedback.
            comment = 'Default bounds'
            if lower != None and upper != None:
                comment = 'User supplied lower and upper bound'
            elif lower != None:
                comment = 'User supplied lower bound'
            elif upper != None:
                comment = 'User supplied upper bound'

            # Alias the number of increments for this parameter.
            incs = model_inc[-1][i]

            # Error checking for increment values of None.
            if incs == None and values[i] in [None, {}, []]:
                raise RelaxError(
                    "The parameter '%s' has no preset value, therefore a grid increment of None is not valid."
                    % names[i])

            # The lower bound for this parameter.
            if lower != None:
                lower_i = lower[i]
            else:
                lower_i = param_object.grid_lower(names[i],
                                                  incs=incs,
                                                  model_info=model_info)

            # The upper bound for this parameter.
            if upper != None:
                upper_i = upper[i]
            else:
                upper_i = param_object.grid_upper(names[i],
                                                  incs=incs,
                                                  model_info=model_info)

            # The skipping logic.
            skip = False
            if skip_preset:
                # Override the flag if the zoom is on.
                if zoom:
                    skip = False

                # No preset value.
                elif values[i] in [None, {}, []]:
                    skip = False

                # The preset value is a NaN value due to numpy conversions of None.
                elif isNaN(values[i]):
                    skip = False

                # Ok, now the parameter can be skipped.
                else:
                    skip = True

            # Override the skip flag if the incs value is None.
            if incs == None:
                skip = True

            # Skip preset values.
            if skip:
                lower_i = values[i]
                upper_i = values[i]
                model_inc[-1][i] = incs = 1
                comment = 'Preset value'

            # Zooming grid.
            elif zoom:
                # The full size and scaled size.
                size = upper_i - lower_i
                zoom_size = size * zoom_factor
                half_size = zoom_size / 2.0
                comment = 'Zoom grid width of %s %s' % (
                    zoom_size, param_object.units(names[i]))

                # The new size around the current value.
                lower_zoom = values[i] - half_size
                upper_zoom = values[i] + half_size

                # Outside of the original lower bound, so shift the grid to fit.
                if zoom > 0 and lower_zoom < lower_i:
                    # The amount to shift by.
                    shift = lower_i - lower_zoom

                    # Set the new bounds.
                    upper_i = upper_zoom + shift

                # Outside of the original upper bound, so shift the grid to fit.
                elif zoom > 0 and upper_zoom > upper_i:
                    # The amount to shift by.
                    shift = upper_i - upper_zoom

                    # Set the new bounds.
                    lower_i = lower_zoom + shift

                # Inside the original bounds.
                else:
                    lower_i = lower_zoom
                    upper_i = upper_zoom

            # Add to the data list for printing out.
            data.append([
                names[i],
                "%15s" % lower_i,
                "%15s" % upper_i,
                "%15s" % incs, comment
            ])

            # Scale the bounds.
            scaling = param_object.scaling(names[i], model_info=model_info)
            lower_i /= scaling
            upper_i /= scaling

            # Append.
            model_lower[-1].append(lower_i)
            model_upper[-1].append(upper_i)

        # Printout.
        if verbosity:
            write_data(out=sys.stdout,
                       headings=[
                           "Parameter", "Lower bound", "Upper bound",
                           "Increments", "Comment"
                       ],
                       data=data)
            sys.stdout.write('\n')

    # Return the bounds.
    return model_lower, model_upper, model_inc
예제 #21
0
def print_frame_order_2nd_degree(daeg, name=None, epsilon=1e-15, integer=False, dot=False, comma=True, file=None):
    """Nicely print out the Frame Order matrix of the 2nd degree.

    @param daeg:        The 3D, rank-4 Frame Order matrix.
    @type daeg:         numpy 3D, rank-4 array
    @keyword name:      The name of the matrix.
    @type name:         None or str
    @keyword epsilon:   The minimum value, below which is considered zero.
    @type epsilon:      float
    @keyword integer:   A flag which if true will only print the integer part of the number.
    @type integer:      bool
    @keyword dot:       A flag which if true replaces all zeros with dot characters.
    @type dot:          bool
    @keyword comma:     A flag which if true causes commas to be printed between the elements.
    @type comma:        bool
    @keyword file:      The file object to write to.
    @type file:         file object
    """

    # Default name.
    if not name:
        name = 'Frame Order matrix, 2nd degree'

    # No file given, so send to STDOUT.
    if file == None:
        file = sys.stdout

    # Header and first row start.
    file.write("\n%s:\n" % name)
    file.write('[[')

    # Convert to an array, if necessary.
    if isinstance(daeg, matrix):
        daeg = array(daeg)

    # Loop over the rows.
    for i in range(len(daeg)):
        # 2nd to last row start.
        if i != 0:
            file.write(' [')

        # Row end character.
        char2 = ''
        if comma:
            char2 = ','
        if i == len(daeg) - 1:
            char2 = ']'

        # Loop over the columns.
        for j in range(len(daeg[i])):
            # Column end character.
            char1 = ''
            if comma:
                char1 = ','
            if j == len(daeg[i]) - 1:
                char1 = ']%s\n' % char2

            # Write out the elements.
            if abs(daeg[i, j]) > epsilon:
                # Integer printout.
                if integer:
                    val = int(daeg[i, j])
                    format = "%4i%s"

                # Float printout.
                else:
                    val = daeg[i, j]
                    format = "%10.4f%s"

            # NaN.
            elif isNaN(daeg[i, j]):
                val = 'NaN'
                if integer:
                    format = "%4i%s"
                else:
                    format = "%10s%s"

            # Write out the zero elements.
            else:
                # Integer printout.
                if integer:
                    format = "%4s%s"

                # Float printout.
                else:
                    format = "%10s%s"

                # The character.
                if dot:
                    val = '.'
                else:
                    val = '0'

            # Write.
            file.write(format % (val, char1))
예제 #22
0
def disassemble_result(param_vector=None,
                       func=None,
                       iter=None,
                       fc=None,
                       gc=None,
                       hc=None,
                       warning=None,
                       spin=None,
                       sim_index=None,
                       model_type=None,
                       scaling_matrix=None):
    """Disassemble the optimisation results.

    @keyword param_vector:      The model-free parameter vector.
    @type param_vector:         numpy array
    @keyword func:              The optimised chi-squared value.
    @type func:                 float
    @keyword iter:              The number of optimisation steps required to find the minimum.
    @type iter:                 int
    @keyword fc:                The function count.
    @type fc:                   int
    @keyword gc:                The gradient count.
    @type gc:                   int
    @keyword hc:                The Hessian count.
    @type hc:                   int
    @keyword warning:           Any optimisation warnings.
    @type warning:              str or None
    @keyword spin:              The spin container.
    @type spin:                 SpinContainer instance or None
    @keyword sim_index:         The Monte Carlo simulation index.
    @type sim_index:            int or None
    @keyword model_type:        The model-free model type, one of 'mf', 'local_tm', 'diff', or
                                'all'.
    @type model_type:           str
    @keyword scaling_matrix:    The diagonal, square scaling matrix.
    @type scaling_matrix:       numpy diagonal matrix
    """

    # No result.
    if param_vector is None:
        return

    # Alias the current data pipe.
    cdp = pipes.get_pipe()

    # Catch infinite chi-squared values.
    if isInf(func):
        raise RelaxInfError('chi-squared')

    # Catch chi-squared values of NaN.
    if isNaN(func):
        raise RelaxNaNError('chi-squared')

    # Scaling.
    if scaling_matrix is not None:
        param_vector = dot(scaling_matrix, param_vector)

    # Check if the chi-squared value is lower.  This allows for a parallelised grid search!
    if sim_index == None:
        # Get the correct value.
        chi2 = None
        if (model_type == 'mf' or model_type == 'local_tm') and hasattr(
                cdp, 'chi2'):
            chi2 = spin.chi2
        if (model_type == 'diff' or model_type == 'all') and hasattr(
                cdp, 'chi2'):
            chi2 = cdp.chi2

        # Spin text.
        spin_text = ''
        if spin != None and hasattr(spin, '_spin_ids') and len(spin._spin_ids):
            spin_text = " for the spin '%s'" % spin._spin_ids[0]

        # No improvement.
        if chi2 != None and func >= chi2:
            print(
                "Discarding the optimisation results%s, the optimised chi-squared value is higher than the current value (%s >= %s)."
                % (spin_text, func, chi2))

            # Exit!
            return

        # New minimum.
        else:
            print(
                "Storing the optimisation results%s, the optimised chi-squared value is lower than the current value (%s < %s)."
                % (spin_text, func, chi2))

    # Disassemble the parameter vector.
    disassemble_param_vector(model_type,
                             param_vector=param_vector,
                             spin=spin,
                             sim_index=sim_index)

    # Monte Carlo minimisation statistics.
    if sim_index != None:
        # Sequence specific minimisation statistics.
        if model_type == 'mf' or model_type == 'local_tm':

            # Chi-squared statistic.
            spin.chi2_sim[sim_index] = func

            # Iterations.
            spin.iter_sim[sim_index] = iter

            # Function evaluations.
            spin.f_count_sim[sim_index] = fc

            # Gradient evaluations.
            spin.g_count_sim[sim_index] = gc

            # Hessian evaluations.
            spin.h_count_sim[sim_index] = hc

            # Warning.
            spin.warning_sim[sim_index] = warning

        # Global minimisation statistics.
        elif model_type == 'diff' or model_type == 'all':
            # Chi-squared statistic.
            cdp.chi2_sim[sim_index] = func

            # Iterations.
            cdp.iter_sim[sim_index] = iter

            # Function evaluations.
            cdp.f_count_sim[sim_index] = fc

            # Gradient evaluations.
            cdp.g_count_sim[sim_index] = gc

            # Hessian evaluations.
            cdp.h_count_sim[sim_index] = hc

            # Warning.
            cdp.warning_sim[sim_index] = warning

    # Normal statistics.
    else:
        # Sequence specific minimisation statistics.
        if model_type == 'mf' or model_type == 'local_tm':
            # Chi-squared statistic.
            spin.chi2 = func

            # Iterations.
            spin.iter = iter

            # Function evaluations.
            spin.f_count = fc

            # Gradient evaluations.
            spin.g_count = gc

            # Hessian evaluations.
            spin.h_count = hc

            # Warning.
            spin.warning = warning

        # Global minimisation statistics.
        elif model_type == 'diff' or model_type == 'all':
            # Chi-squared statistic.
            cdp.chi2 = func

            # Iterations.
            cdp.iter = iter

            # Function evaluations.
            cdp.f_count = fc

            # Gradient evaluations.
            cdp.g_count = gc

            # Hessian evaluations.
            cdp.h_count = hc

            # Warning.
            cdp.warning = warning
예제 #23
0
def r2eff_ns_mmq_2site_sq_dq_zq(M0=None,
                                F_vector=array([1, 0], float64),
                                R20A=None,
                                R20B=None,
                                pA=None,
                                dw=None,
                                dwH=None,
                                kex=None,
                                inv_tcpmg=None,
                                tcp=None,
                                back_calc=None,
                                num_points=None,
                                power=None):
    """The 2-site numerical solution to the Bloch-McConnell equation for SQ, ZQ, and DQ data.

    The notation used here comes from:

        - Dmitry M. Korzhnev, Philipp Neudecker, Anthony Mittermaier, Vladislav Yu. Orekhov, and Lewis E. Kay (2005).  Multiple-site exchange in proteins studied with a suite of six NMR relaxation dispersion experiments: An application to the folding of a Fyn SH3 domain mutant.  J. Am. Chem. Soc., 127, 15602-15611.  (doi:  http://dx.doi.org/10.1021/ja054550e).

    This function calculates and stores the R2eff values.


    @keyword M0:            This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    @type M0:               numpy float64, rank-1, 7D array
    @keyword F_vector:      The observable magnitisation vector.  This defaults to [1, 0] for X observable magnitisation.
    @type F_vector:         numpy rank-1, 2D float64 array
    @keyword R20A:          The transverse, spin-spin relaxation rate for state A.
    @type R20A:             numpy float array of rank [NS][NM][NO][ND]
    @keyword R20B:          The transverse, spin-spin relaxation rate for state B.
    @type R20B:             numpy float array of rank [NS][NM][NO][ND]
    @keyword pA:            The population of state A.
    @type pA:               float
    @keyword dw:            The combined chemical exchange difference between states A and B in rad/s.  It should be set to dwH for 1H SQ data, dw for heteronuclear SQ data, dwH-dw for ZQ data, and dwH+dw for DQ data.
    @type dw:               numpy float array of rank [NS][NM][NO][ND]
    @keyword dwH:           Unused - this is simply to match the r2eff_ns_mmq_2site_mq() function arguments.
    @type dwH:              numpy float array of rank [NS][NM][NO][ND]
    @keyword kex:           The kex parameter value (the exchange rate in rad/s).
    @type kex:              float
    @keyword inv_tcpmg:     The inverse of the total duration of the CPMG element (in inverse seconds).
    @type inv_tcpmg:        numpy float array of rank [NS][NM][NO][ND]
    @keyword tcp:           The tau_CPMG times (1 / 4.nu1).
    @type tcp:              numpy float array of rank [NS][NM][NO][ND]
    @keyword back_calc:     The array for holding the back calculated R2eff values.  Each element corresponds to one of the CPMG nu1 frequencies.
    @type back_calc:        numpy float array of rank [NS][NM][NO][ND]
    @keyword num_points:    The number of points on the dispersion curve, equal to the length of the tcp and back_calc arguments.
    @type num_points:       numpy int array of rank [NS][NM][NO]
    @keyword power:         The matrix exponential power array.
    @type power:            numpy int array of rank [NS][NM][NO][ND]
    """

    # Once off parameter conversions.
    pB = 1.0 - pA
    k_BA = pA * kex
    k_AB = pB * kex

    # This is a vector that contains the initial magnetizations corresponding to the A and B state transverse magnetizations.
    M0[0] = pA
    M0[1] = pB

    # Extract shape of experiment.
    NS, NM, NO = num_points.shape

    # Populate the m1 and m2 matrices (only once per function call for speed).
    m1_mat = rmmq_2site_rankN(R20A=R20A,
                              R20B=R20B,
                              dw=dw,
                              k_AB=k_AB,
                              k_BA=k_BA,
                              tcp=tcp)
    m2_mat = rmmq_2site_rankN(R20A=R20A,
                              R20B=R20B,
                              dw=-dw,
                              k_AB=k_AB,
                              k_BA=k_BA,
                              tcp=tcp)

    # The A+/- matrices.
    A_pos_mat = matrix_exponential(m1_mat, dtype=complex128)
    A_neg_mat = matrix_exponential(m2_mat, dtype=complex128)

    # The evolution for one n.
    evol_block_mat = einsum('...ij, ...jk', A_neg_mat, A_pos_mat)
    evol_block_mat = einsum('...ij, ...jk', A_neg_mat, evol_block_mat)
    evol_block_mat = einsum('...ij, ...jk', A_pos_mat, evol_block_mat)

    # Loop over spins.
    for si in range(NS):
        # Loop over the spectrometer frequencies.
        for mi in range(NM):
            # Loop over offsets:
            for oi in range(NO):
                # Extract number of points.
                num_points_i = num_points[si, mi, oi]

                # Loop over the time points, back calculating the R2eff values.
                for i in range(num_points_i):
                    # Extract data from array.
                    power_i = int(power[si, mi, oi, i])
                    evol_block_i = evol_block_mat[si, mi, oi, i]

                    # The full evolution.
                    evol = matrix_power(evol_block_i, power_i)

                    # The next lines calculate the R2eff using a two-point approximation, i.e. assuming that the decay is mono-exponential.
                    Mx = dot(F_vector, dot(evol, M0))
                    Mx = Mx.real
                    if Mx <= 0.0 or isNaN(Mx):
                        back_calc[si, mi, oi, i] = 1e99
                    else:
                        back_calc[si, mi, oi,
                                  i] = -inv_tcpmg[si, mi, oi, i] * log(Mx / pA)
예제 #24
0
def print_frame_order_2nd_degree(daeg,
                                 name=None,
                                 places=4,
                                 epsilon=1e-15,
                                 integer=False,
                                 dot=False,
                                 comma=True,
                                 file=None):
    """Nicely print out the Frame Order matrix of the 2nd degree.

    @param daeg:        The 3D, rank-4 Frame Order matrix.
    @type daeg:         numpy 3D, rank-4 array
    @keyword name:      The name of the matrix.
    @type name:         None or str
    @keyword places:    The number of decimal places to print.
    @type places:       int
    @keyword epsilon:   The minimum value, below which is considered zero.
    @type epsilon:      float
    @keyword integer:   A flag which if true will only print the integer part of the number.
    @type integer:      bool
    @keyword dot:       A flag which if true replaces all zeros with dot characters.
    @type dot:          bool
    @keyword comma:     A flag which if true causes commas to be printed between the elements.
    @type comma:        bool
    @keyword file:      The file object to write to.
    @type file:         file object
    """

    # Default name.
    if not name:
        name = 'Frame Order matrix, 2nd degree'

    # No file given, so send to STDOUT.
    if file == None:
        file = sys.stdout

    # Header and first row start.
    file.write("\n%s:\n" % name)
    file.write('[[')

    # Convert to an array, if necessary.
    if isinstance(daeg, matrix):
        daeg = array(daeg)

    # Loop over the rows.
    for i in range(len(daeg)):
        # 2nd to last row start.
        if i != 0:
            file.write(' [')

        # Row end character.
        char2 = ''
        if comma:
            char2 = ','
        if i == len(daeg) - 1:
            char2 = ']'

        # Loop over the columns.
        for j in range(len(daeg[i])):
            # Column end character.
            char1 = ''
            if comma:
                char1 = ','
            if j == len(daeg[i]) - 1:
                char1 = ']%s\n' % char2

            # Write out the elements.
            if abs(daeg[i, j]) > epsilon:
                # Integer printout.
                if integer:
                    val = int(daeg[i, j])
                    format = "%" + repr(places) + "i%s"

                # Float printout.
                else:
                    val = daeg[i, j]
                    format = "%" + repr(places +
                                        6) + "." + repr(places) + "f%s"

            # NaN.
            elif isNaN(daeg[i, j]):
                val = 'NaN'
                if integer:
                    format = "%" + repr(places) + "i%s"
                else:
                    format = "%" + repr(places + 6) + "s%s"

            # Write out the zero elements.
            else:
                # Integer printout.
                if integer:
                    format = "%" + repr(places) + "s%s"

                # Float printout.
                else:
                    format = "%" + repr(places + 6) + "s%s"

                # The character.
                if dot:
                    val = '.'
                else:
                    val = '0'

            # Write.
            file.write(format % (val, char1))