def run(self, processor, completed): """Set up and perform the optimisation.""" # Print out. if self.verbosity >= 1: # Individual spin block section. top = 2 if self.verbosity >= 2: top += 2 subsection(file=sys.stdout, text="Fitting to the spin block %s"%self.spin_ids, prespace=top) # Grid search printout. if search('^[Gg]rid', self.min_algor): result = 1 for x in self.inc: result = mul(result, x) print("Unconstrained grid search size: %s (constraints may decrease this size).\n" % result) # Initialise the function to minimise. model = Dispersion(model=self.spins[0].model, num_params=self.param_num, num_spins=count_spins(self.spins), num_frq=len(self.fields), exp_types=self.exp_types, values=self.values, errors=self.errors, missing=self.missing, frqs=self.frqs, frqs_H=self.frqs_H, cpmg_frqs=self.cpmg_frqs, spin_lock_nu1=self.spin_lock_nu1, chemical_shifts=self.chemical_shifts, offset=self.offsets, tilt_angles=self.tilt_angles, r1=self.r1, relax_times=self.relax_times, scaling_matrix=self.scaling_matrix, r1_fit=self.r1_fit) # Grid search. if search('^[Gg]rid', self.min_algor): results = grid(func=model.func, args=(), num_incs=self.inc, lower=self.lower, upper=self.upper, A=self.A, b=self.b, verbosity=self.verbosity) # Unpack the results. param_vector, chi2, iter_count, warning = results f_count = iter_count g_count = 0.0 h_count = 0.0 # Minimisation. else: results = generic_minimise(func=model.func, args=(), x0=self.param_vector, min_algor=self.min_algor, min_options=self.min_options, func_tol=self.func_tol, grad_tol=self.grad_tol, maxiter=self.max_iterations, A=self.A, b=self.b, full_output=True, print_flag=self.verbosity) # Unpack the results. if results == None: return param_vector, chi2, iter_count, f_count, g_count, h_count, warning = results # Optimisation printout. if self.verbosity: print("\nOptimised parameter values:") for i in range(len(param_vector)): print("%-20s %25.15f" % (self.param_names[i], param_vector[i]*self.scaling_matrix[i, i])) # Create the result command object to send back to the master. processor.return_object(Disp_result_command(processor=processor, memo_id=self.memo_id, param_vector=param_vector, chi2=chi2, iter_count=iter_count, f_count=f_count, g_count=g_count, h_count=h_count, warning=warning, missing=self.missing, back_calc=model.get_back_calc(), completed=False))
def __init__(self, exp_type=None, num_spins=1, model=None, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Special method __init__() is called first (acts as Constructor). It brings in data from outside the class like the variable num_spins (in this case num_spins is also set to a default value of 1). The first parameter of any method/function in the class is always self, the name self is used by convention. Assigning num_spins to self.num_spins allows it to be passed to all methods within the class. Think of self as a carrier, or if you want impress folks call it target instance object. @keyword exp_type: The list of experiment types. @type exp_type: list of str @keyword num_spins: Number of spins in the cluster. @type num_spins: integer @keyword model: The dispersion model to instantiate the Dispersion class with. @type model: string @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings """ # Define parameters self.exp_type = exp_type self.model = model self.num_spins = num_spins #self.fields = array([800. * 1E6]) #self.fields = array([600. * 1E6, 800. * 1E6]) self.fields = array([600. * 1E6, 800. * 1E6, 900. * 1E6]) # Set The spin-lock field strength, nu1, in Hz self.spin_lock_fields = [431.0, 651.2, 800.5, 984.0, 1341.11] # Required data structures. self.relax_times = self.fields / (100 * 100. * 1E6) self.ncycs = [] self.points = [] self.value = [] self.error = [] for i in range(len(self.fields)): ncyc = arange(2, 1000. * self.relax_times[i], 4) #ncyc = arange(2, 42, 2) self.ncycs.append(ncyc) print("sfrq: ", self.fields[i], "number of cpmg frq", len(ncyc), ncyc) # CPMG data. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: cpmg_point = ncyc / self.relax_times[i] self.points.append(list(cpmg_point)) self.value.append([2.0] * len(cpmg_point)) self.error.append([1.0] * len(cpmg_point)) # R1rho data. else: points = self.spin_lock_fields self.points.append(list(points)) self.value.append([2.0] * len(self.spin_lock_fields)) self.error.append([1.0] * len(self.spin_lock_fields)) # Spin lock offsets in ppm. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: self.offsets = [0] else: self.offsets = list(range(10)) # Chemical shift in ppm. self.chemical_shift = 1.0 # Assemble param vector. self.params = self.assemble_param_vector(r2=r2, r2a=r2a, r2b=r2b, phi_ex=phi_ex, phi_ex_B=phi_ex_B, phi_ex_C=phi_ex_C, dw=dw, dw_AB=dw_AB, dw_BC=dw_BC, dwH=dwH, dwH_AB=dwH_AB, dwH_BC=dwH_BC, pA=pA, pB=pB, kex=kex, kex_AB=kex_AB, kex_BC=kex_BC, kex_AC=kex_AC, kB=kB, kC=kC, k_AB=k_AB, tex=tex, spins_params=spins_params) # Make nested list arrays of data. And return them. values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, spin_lock_nu1 = self.return_r2eff_arrays( ) # The offset and R1 data. chemical_shifts, offsets, tilt_angles, Delta_omega, w_eff = self.return_offset_data( ) r1 = ones([self.num_spins, self.fields.shape[0]]) # relax version compatibility. self.relax_times_compat = relax_times if version == 'repository checkout' or version_comparison( version, '3.2.3') == 1: self.relax_times_compat = [] for ei in range(len(self.exp_type)): self.relax_times_compat.append([]) for mi in range(len(self.fields)): self.relax_times_compat[ei].append([]) for oi in range(len(self.offsets)): self.relax_times_compat[ei][mi].append([]) for di in range(len(self.points[mi])): self.relax_times_compat[ei][mi][oi].append( self.relax_times.tolist()) # Init the Dispersion class. self.model = Dispersion(model=self.model, num_params=None, num_spins=self.num_spins, num_frq=len(self.fields), exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=self.relax_times_compat, scaling_matrix=None)
class Profile(Dispersion): """Class Profile inherits the Dispersion container class object.""" def __init__(self, exp_type=None, num_spins=1, model=None, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Special method __init__() is called first (acts as Constructor). It brings in data from outside the class like the variable num_spins (in this case num_spins is also set to a default value of 1). The first parameter of any method/function in the class is always self, the name self is used by convention. Assigning num_spins to self.num_spins allows it to be passed to all methods within the class. Think of self as a carrier, or if you want impress folks call it target instance object. @keyword exp_type: The list of experiment types. @type exp_type: list of str @keyword num_spins: Number of spins in the cluster. @type num_spins: integer @keyword model: The dispersion model to instantiate the Dispersion class with. @type model: string @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings """ # Define parameters self.exp_type = exp_type self.model = model self.num_spins = num_spins #self.fields = array([800. * 1E6]) #self.fields = array([600. * 1E6, 800. * 1E6]) self.fields = array([600. * 1E6, 800. * 1E6, 900. * 1E6]) # Set The spin-lock field strength, nu1, in Hz self.spin_lock_fields = [431.0, 651.2, 800.5, 984.0, 1341.11] # Required data structures. self.relax_times = self.fields / (100 * 100. * 1E6) self.ncycs = [] self.points = [] self.value = [] self.error = [] for i in range(len(self.fields)): ncyc = arange(2, 1000. * self.relax_times[i], 4) #ncyc = arange(2, 42, 2) self.ncycs.append(ncyc) print("sfrq: ", self.fields[i], "number of cpmg frq", len(ncyc), ncyc) # CPMG data. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: cpmg_point = ncyc / self.relax_times[i] self.points.append(list(cpmg_point)) self.value.append([2.0] * len(cpmg_point)) self.error.append([1.0] * len(cpmg_point)) # R1rho data. else: points = self.spin_lock_fields self.points.append(list(points)) self.value.append([2.0] * len(self.spin_lock_fields)) self.error.append([1.0] * len(self.spin_lock_fields)) # Spin lock offsets in ppm. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: self.offsets = [0] else: self.offsets = list(range(10)) # Chemical shift in ppm. self.chemical_shift = 1.0 # Assemble param vector. self.params = self.assemble_param_vector(r2=r2, r2a=r2a, r2b=r2b, phi_ex=phi_ex, phi_ex_B=phi_ex_B, phi_ex_C=phi_ex_C, dw=dw, dw_AB=dw_AB, dw_BC=dw_BC, dwH=dwH, dwH_AB=dwH_AB, dwH_BC=dwH_BC, pA=pA, pB=pB, kex=kex, kex_AB=kex_AB, kex_BC=kex_BC, kex_AC=kex_AC, kB=kB, kC=kC, k_AB=k_AB, tex=tex, spins_params=spins_params) # Make nested list arrays of data. And return them. values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, spin_lock_nu1 = self.return_r2eff_arrays( ) # The offset and R1 data. chemical_shifts, offsets, tilt_angles, Delta_omega, w_eff = self.return_offset_data( ) r1 = ones([self.num_spins, self.fields.shape[0]]) # relax version compatibility. self.relax_times_compat = relax_times if version == 'repository checkout' or version_comparison( version, '3.2.3') == 1: self.relax_times_compat = [] for ei in range(len(self.exp_type)): self.relax_times_compat.append([]) for mi in range(len(self.fields)): self.relax_times_compat[ei].append([]) for oi in range(len(self.offsets)): self.relax_times_compat[ei][mi].append([]) for di in range(len(self.points[mi])): self.relax_times_compat[ei][mi][oi].append( self.relax_times.tolist()) # Init the Dispersion class. self.model = Dispersion(model=self.model, num_params=None, num_spins=self.num_spins, num_frq=len(self.fields), exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=self.relax_times_compat, scaling_matrix=None) def return_offset_data(self): """Return numpy arrays of the chemical shifts, offsets and tilt angles. @keyword field_count: The number of spectrometer field strengths. This may not be equal to the length of the fields list as the user may not have set the field strength. @type field_count: int @keyword fields: The spin-lock field strengths to use instead of the user loaded values - to enable interpolation. The dimensions are {Ei, Mi}. @type fields: rank-2 list of floats """ # spins=[spin], spin_ids=[spin_id], field_count=field_count, fields=spin_lock_nu1 # Initialise the data structures for the target function. shifts = [] offsets = [] theta = [] Domega = [] w_e = [] for ei in range(len(self.exp_type)): shifts.append([]) offsets.append([]) theta.append([]) Domega.append([]) w_e.append([]) for si in range(self.num_spins): shifts[ei].append([]) offsets[ei].append([]) theta[ei].append([]) Domega[ei].append([]) w_e[ei].append([]) for mi in range(len(self.fields)): shifts[ei][si].append(None) offsets[ei][si].append([]) theta[ei][si].append([]) Domega[ei][si].append([]) w_e[ei][si].append([]) for oi in range(len(self.offsets)): offsets[ei][si][mi].append(None) theta[ei][si][mi].append([]) Domega[ei][si][mi].append([]) w_e[ei][si][mi].append([]) # Assemble the data. si = 0 for spin_index in range(self.num_spins): for ei in range(len(self.exp_type)): exp_type = self.exp_type[ei] # Add the experiment type. for mi in range(len(self.fields)): # Get the frq. frq = self.fields[mi] for oi in range(len(self.offsets)): # The spin-lock data. # Convert the shift from ppm to rad/s and store it. shifts[ei][si][ mi] = self.chemical_shift * 2.0 * pi * frq / g1H * g15N * 1e-6 # Set The spin-lock offset, omega_rf, in ppm. offset = self.offsets[oi] # Store the offset in rad/s. Only once and using the first key. offsets[ei][si][mi][ oi] = offset * 2.0 * pi * frq / g1H * g15N * 1e-6 # Loop over the dispersion points. for di in range(len(self.spin_lock_fields)): # Alias the point. point = self.spin_lock_fields[di] # Skip reference spectra. if point == None: continue # Calculate the tilt angle. omega1 = point * 2.0 * pi Delta_omega = shifts[ei][si][mi] - offsets[ei][si][ mi][oi] Domega[ei][si][mi][oi].append(Delta_omega) if Delta_omega == 0.0: theta[ei][si][mi][oi].append(pi / 2.0) # Calculate the theta angle describing the tilted rotating frame relative to the laboratory. # theta = atan(omega1 / Delta_omega). # If Delta_omega is negative, there follow the symmetry of atan, that atan(-x) = - atan(x). # Then it should be: theta = pi + atan(-x) = pi - atan(x) = pi - abs(atan( +/- x)). # This is taken care of with the atan2(y, x) function, which return atan(y / x), in radians, and the result is between -pi and pi. else: theta[ei][si][mi][oi].append( atan2(omega1, Delta_omega)) # Calculate effective field in rotating frame w_eff = sqrt(Delta_omega * Delta_omega + omega1 * omega1) w_e[ei][si][mi][oi].append(w_eff) # Increment the spin index. si += 1 # Return the structures. return shifts, offsets, theta, Domega, w_e def return_r2eff_arrays(self): """Return numpy arrays of the R2eff/R1rho values and errors. @return: The numpy array structures of the R2eff/R1rho values, errors, missing data, and corresponding Larmor frequencies. For each structure, the first dimension corresponds to the experiment types, the second the spins of a spin block, the third to the spectrometer field strength, and the fourth is the dispersion points. For the Larmor frequency structure, the fourth dimension is omitted. For R1rho-type data, an offset dimension is inserted between the spectrometer field strength and the dispersion points. @rtype: lists of numpy float arrays, lists of numpy float arrays, lists of numpy float arrays, numpy rank-2 int array """ # Initialise the data structures for the target function. exp_types = [] values = [] errors = [] missing = [] frqs = [] frqs_H = [] relax_times = [] offsets = [] for ei in range(len(self.exp_type)): values.append([]) errors.append([]) missing.append([]) frqs.append([]) frqs_H.append([]) relax_times.append([]) offsets.append([]) for si in range(self.num_spins): values[ei].append([]) errors[ei].append([]) missing[ei].append([]) frqs[ei].append([]) frqs_H[ei].append([]) offsets[ei].append([]) for mi in range(len(self.fields)): values[ei][si].append([]) errors[ei][si].append([]) missing[ei][si].append([]) frqs[ei][si].append(0.0) frqs_H[ei][si].append(0.0) offsets[ei][si].append([]) for oi in range(len(self.offsets)): values[ei][si][mi].append([]) errors[ei][si][mi].append([]) missing[ei][si][mi].append([]) offsets[ei][si][mi].append([]) for mi in range(len(self.fields)): relax_times[ei].append(None) cpmg_frqs = [] for ei in range(len(self.exp_type)): cpmg_frqs.append([]) for mi in range(len(self.fields)): cpmg_frqs[ei].append([]) for oi in range(len(self.offsets)): #cpmg_frqs[ei][mi].append(self.points) cpmg_frqs[ei][mi].append([]) spin_lock_nu1 = [] for ei in range(len(self.exp_type)): spin_lock_nu1.append([]) for mi in range(len(self.fields)): spin_lock_nu1[ei].append([]) for oi in range(len(self.offsets)): #cpmg_frqs[ei][mi].append(self.points) spin_lock_nu1[ei][mi].append([]) # Pack the R2eff/R1rho data. si = 0 for spin_index in range(self.num_spins): data_flag = True for ei in range(len(self.exp_type)): exp_type = self.exp_type[ei] # Add the experiment type. if exp_type not in exp_types: exp_types.append(exp_type) for mi in range(len(self.fields)): # Get the frq. frq = self.fields[mi] # The Larmor frequency for this spin (and that of an attached proton for the MMQ models) and field strength (in MHz*2pi to speed up the ppm to rad/s conversion). frqs[ei][si][mi] = 2.0 * pi * frq / g1H * g15N * 1e-6 frqs_H[ei][si][mi] = 2.0 * pi * frq * 1e-6 for oi in range(len(self.offsets)): # CPMG data. if exp_type == EXP_TYPE_CPMG_SQ or EXP_TYPE_CPMG_MQ in self.exp_type: # Get the cpmg frq. cpmg_frqs[ei][mi][oi] = self.points[mi] back_calc = array([0.0] * len(cpmg_frqs[ei][mi][oi])) # R1rho data. else: # Get the spin_lock_nu1 frq. spin_lock_nu1[ei][mi][oi] = self.points[mi] back_calc = array([0.0] * len(spin_lock_nu1[ei][mi][oi])) for di in range(len(self.points[mi])): missing[ei][si][mi][oi].append(0) # Values #values[ei][si][mi][oi].append(self.value[mi][di]) values[ei][si][mi][oi].append(back_calc[di]) # The errors. errors[ei][si][mi][oi].append(self.error[mi][di]) #print self.value[mi][di], self.error[mi][di] # The relaxation times. # Found. relax_time = self.relax_times[mi] # Store the time. relax_times[ei][mi] = relax_time # Increment the spin index. si += 1 # Convert to numpy arrays. relax_times = array(relax_times, float64) for ei in range(len(self.exp_type)): for si in range(self.num_spins): for mi in range(len(self.fields)): for oi in range(len(self.offsets)): cpmg_frqs[ei][mi][oi] = array(cpmg_frqs[ei][mi][oi], float64) spin_lock_nu1[ei][mi][oi] = array( spin_lock_nu1[ei][mi][oi], float64) values[ei][si][mi][oi] = array(values[ei][si][mi][oi], float64) errors[ei][si][mi][oi] = array(errors[ei][si][mi][oi], float64) missing[ei][si][mi][oi] = array( missing[ei][si][mi][oi], int32) # Return the structures. return values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, asarray( spin_lock_nu1) def assemble_param_vector(self, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Assemble the dispersion relaxation dispersion curve fitting parameter vector. @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings @return: An array of the parameter values of the dispersion relaxation model. @rtype: numpy float array """ # Initialise. param_vector = [] # Loop over the parameters of the cluster. for param_name, spin_index, mi in self.loop_parameters( spins_params=spins_params): if param_name == 'r2': value = r2 value = value + mi + spin_index * 0.1 elif param_name == 'r2a': value = r2a value = value + mi + spin_index * 0.1 elif param_name == 'r2b': value = r2b value = value + mi + spin_index * 0.1 elif param_name == 'phi_ex': value = phi_ex + spin_index elif param_name == 'phi_ex_B': value = phi_ex_B + spin_index elif param_name == 'phi_ex_C': value = phi_ex_C + spin_index elif param_name == 'dw': value = dw + spin_index elif param_name == 'dw_AB': value = dw_AB + spin_index elif param_name == 'dw_BC': value = dw_BC + spin_index elif param_name == 'dwH': value = dwH + spin_index elif param_name == 'dwH_AB': value = dw_AB + spin_index elif param_name == 'dwH_BC': value = dw_BC + spin_index elif param_name == 'pA': value = pA elif param_name == 'pB': value = pB elif param_name == 'kex': value = kex elif param_name == 'kex_AB': value = kex_AB elif param_name == 'kex_BC': value = kex_BC elif param_name == 'kex_AC': value = kex_AC elif param_name == 'kB': value = kB elif param_name == 'kC': value = kC elif param_name == 'k_AB': value = k_AB elif param_name == 'tex': value = tex # Add to the vector. param_vector.append(value) # Return a numpy array. return array(param_vector, float64) def loop_parameters(self, spins_params=None): """Generator function for looping of the parameters of the cluster. @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings @return: The parameter name. @rtype: str """ # Loop over the parameters of the cluster. # First the R2 parameters (one per spin per field strength). for spin_index in range(self.num_spins): # The R2 parameter. if 'r2' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2', spin_index, mi # The R2A parameter. if 'r2a' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2a', spin_index, mi # The R2B parameter. if 'r2b' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2b', spin_index, mi # Then the chemical shift difference parameters 'phi_ex', 'phi_ex_B', 'phi_ex_C', 'padw2', 'dw', 'dw_AB', 'dw_BC', 'dw_AB' (one per spin). for spin_index in range(self.num_spins): # Yield the data. if 'phi_ex' in spins_params: yield 'phi_ex', spin_index, 0 if 'phi_ex_B' in spins_params: yield 'phi_ex_B', spin_index, 0 if 'phi_ex_C' in spins_params: yield 'phi_ex_C', spin_index, 0 if 'padw2' in spins_params: yield 'padw2', pspin_index, 0 if 'dw' in spins_params: yield 'dw', spin_index, 0 if 'dw_AB' in spins_params: yield 'dw_AB', spin_index, 0 if 'dw_BC' in spins_params: yield 'dw_BC', spin_index, 0 # Then a separate block for the proton chemical shift difference parameters for the MQ models (one per spin). for spin_index in range(self.num_spins): if 'dwH' in spins_params: yield 'dwH', spin_index, 0 if 'dwH_AB' in spins_params: yield 'dwH_AB', spin_index, 0 if 'dwH_BC' in spins_params: yield 'dwH_BC', spin_index, 0 # All other parameters (one per spin cluster). for param in spins_params: if not param in [ 'r2', 'r2a', 'r2b', 'phi_ex', 'phi_ex_B', 'phi_ex_C', 'padw2', 'dw', 'dw_AB', 'dw_BC', 'dw_AB', 'dwH', 'dwH_AB', 'dwH_BC', 'dwH_AB' ]: if param == 'pA': yield 'pA', 0, 0 elif param == 'pB': yield 'pB', 0, 0 elif param == 'kex': yield 'kex', 0, 0 elif param == 'kex_AB': yield 'kex_AB', 0, 0 elif param == 'kex_BC': yield 'kex_BC', 0, 0 elif param == 'kex_AC': yield 'kex_AC', 0, 0 elif param == 'kB': yield 'kB', 0, 0 elif param == 'kC': yield 'kC', 0, 0 elif param == 'k_AB': yield 'k_AB', 0, 0 elif param == 'tex': yield 'tex', 0, 0 def calc(self, params): """Calculate chi2 values. @keyword params: List of parameter strings used in dispersion model. @type params: array of strings @return: Chi2 value. @rtype: float """ # Return chi2 value. chi2 = self.model.func(params) return chi2
def back_calc_r2eff(spin=None, spin_id=None, cpmg_frqs=None, spin_lock_nu1=None, store_chi2=False): """Back-calculation of R2eff/R1rho values for the given spin. @keyword spin: The specific spin data container. @type spin: SpinContainer instance @keyword spin_id: The spin ID string for the spin container. @type spin_id: str @keyword cpmg_frqs: The CPMG frequencies to use instead of the user loaded values - to enable interpolation. @type cpmg_frqs: list of lists of numpy rank-1 float arrays @keyword spin_lock_nu1: The spin-lock field strengths to use instead of the user loaded values - to enable interpolation. @type spin_lock_nu1: list of lists of numpy rank-1 float arrays @keyword store_chi2: A flag which if True will cause the spin specific chi-squared value to be stored in the spin container. @type store_chi2: bool @return: The back-calculated R2eff/R1rho value for the given spin. @rtype: numpy rank-1 float array """ # Skip protons for MMQ data. if spin.model in MODEL_LIST_MMQ and spin.isotope == '1H': return # Create the initial parameter vector. param_vector = assemble_param_vector(spins=[spin]) # Create a scaling matrix. scaling_matrix = assemble_scaling_matrix(spins=[spin], scaling=False) # Number of spectrometer fields. fields = [None] field_count = 1 if hasattr(cdp, 'spectrometer_frq_count'): fields = cdp.spectrometer_frq_list field_count = cdp.spectrometer_frq_count # Initialise the data structures for the target function. values, errors, missing, frqs, frqs_H, exp_types, relax_times = return_r2eff_arrays(spins=[spin], spin_ids=[spin_id], fields=fields, field_count=field_count) # The offset and R1 data. chemical_shifts, offsets, tilt_angles = return_offset_data(spins=[spin], spin_ids=[spin_id], field_count=field_count, fields=spin_lock_nu1) r1 = return_r1_data(spins=[spin], spin_ids=[spin_id], field_count=field_count) # The dispersion data. recalc_tau = True if cpmg_frqs == None and spin_lock_nu1 == None: cpmg_frqs = return_cpmg_frqs(ref_flag=False) spin_lock_nu1 = return_spin_lock_nu1(ref_flag=False) # Reconstruct the structures for interpolation. else: recalc_tau = False values = [] errors = [] missing = [] for exp_type, ei in loop_exp(return_indices=True): values.append([]) errors.append([]) missing.append([]) for si in range(1): values[ei].append([]) errors[ei].append([]) missing[ei].append([]) for frq, mi in loop_frq(return_indices=True): values[ei][si].append([]) errors[ei][si].append([]) missing[ei][si].append([]) for offset, oi in loop_offset(exp_type=exp_type, frq=frq, return_indices=True): if exp_type in EXP_TYPE_LIST_CPMG: num = len(cpmg_frqs[ei][mi][oi]) else: num = len(spin_lock_nu1[ei][mi][oi]) values[ei][si][mi].append(zeros(num, float64)) errors[ei][si][mi].append(ones(num, float64)) missing[ei][si][mi].append(zeros(num, int32)) # Initialise the relaxation dispersion fit functions. model = Dispersion(model=spin.model, num_params=param_num(spins=[spin]), num_spins=1, num_frq=field_count, exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=relax_times, scaling_matrix=scaling_matrix, recalc_tau=recalc_tau) # Make a single function call. This will cause back calculation and the data will be stored in the class instance. chi2 = model.func(param_vector) # Store the chi-squared value. if store_chi2: spin.chi2 = chi2 # Return the structure. return model.back_calc
def back_calc_r2eff(spins=None, spin_ids=None, cpmg_frqs=None, spin_lock_offset=None, spin_lock_nu1=None, relax_times_new=None, store_chi2=False): """Back-calculation of R2eff/R1rho values for the given spin. @keyword spins: The list of specific spin data container for cluster. @type spins: List of SpinContainer instances @keyword spin_ids: The list of spin ID strings for the spin containers in cluster. @type spin_ids: list of str @keyword cpmg_frqs: The CPMG frequencies to use instead of the user loaded values - to enable interpolation. @type cpmg_frqs: list of lists of numpy rank-1 float arrays @keyword spin_lock_offset: The spin-lock offsets to use instead of the user loaded values - to enable interpolation. @type spin_lock_offset: list of lists of numpy rank-1 float arrays @keyword spin_lock_nu1: The spin-lock field strengths to use instead of the user loaded values - to enable interpolation. @type spin_lock_nu1: list of lists of numpy rank-1 float arrays @keyword relax_times_new: The interpolated experiment specific fixed time period for relaxation (in seconds). The dimensions are {Ei, Mi, Oi, Di, Ti}. @type relax_times_new: rank-4 list of floats @keyword store_chi2: A flag which if True will cause the spin specific chi-squared value to be stored in the spin container. @type store_chi2: bool @return: The back-calculated R2eff/R1rho value for the given spin. @rtype: numpy rank-1 float array """ # Create the initial parameter vector. param_vector = assemble_param_vector(spins=spins) # Number of spectrometer fields. fields = [None] field_count = 1 if hasattr(cdp, 'spectrometer_frq_count'): fields = cdp.spectrometer_frq_list field_count = cdp.spectrometer_frq_count # Initialise the data structures for the target function. values, errors, missing, frqs, frqs_H, exp_types, relax_times = return_r2eff_arrays(spins=spins, spin_ids=spin_ids, fields=fields, field_count=field_count) # The offset and R1 data. r1_setup() offsets, spin_lock_fields_inter, chemical_shifts, tilt_angles, Delta_omega, w_eff = return_offset_data(spins=spins, spin_ids=spin_ids, field_count=field_count, spin_lock_offset=spin_lock_offset, fields=spin_lock_nu1) r1 = return_r1_data(spins=spins, spin_ids=spin_ids, field_count=field_count) r1_fit = is_r1_optimised(spins[0].model) # The relaxation times. if relax_times_new != None: relax_times = relax_times_new # The dispersion data. recalc_tau = True if cpmg_frqs == None and spin_lock_nu1 == None and spin_lock_offset == None: cpmg_frqs = return_cpmg_frqs(ref_flag=False) spin_lock_nu1 = return_spin_lock_nu1(ref_flag=False) # Reset the cpmg_frqs if interpolating R1rho models. elif cpmg_frqs == None and spin_lock_offset != None: cpmg_frqs = None spin_lock_nu1 = spin_lock_fields_inter recalc_tau = False values = [] errors = [] missing = [] for exp_type, ei in loop_exp(return_indices=True): values.append([]) errors.append([]) missing.append([]) for si in range(len(spins)): values[ei].append([]) errors[ei].append([]) missing[ei].append([]) for frq, mi in loop_frq(return_indices=True): values[ei][si].append([]) errors[ei][si].append([]) missing[ei][si].append([]) for oi, offset in enumerate(offsets[ei][si][mi]): num = len(spin_lock_nu1[ei][mi][oi]) values[ei][si][mi].append(zeros(num, float64)) errors[ei][si][mi].append(ones(num, float64)) missing[ei][si][mi].append(zeros(num, int32)) # Reconstruct the structures for interpolation. else: recalc_tau = False values = [] errors = [] missing = [] for exp_type, ei in loop_exp(return_indices=True): values.append([]) errors.append([]) missing.append([]) for si in range(len(spins)): values[ei].append([]) errors[ei].append([]) missing[ei].append([]) for frq, mi in loop_frq(return_indices=True): values[ei][si].append([]) errors[ei][si].append([]) missing[ei][si].append([]) for offset, oi in loop_offset(exp_type=exp_type, frq=frq, return_indices=True): if exp_type in EXP_TYPE_LIST_CPMG: num = len(cpmg_frqs[ei][mi][oi]) else: num = len(spin_lock_nu1[ei][mi][oi]) values[ei][si][mi].append(zeros(num, float64)) errors[ei][si][mi].append(ones(num, float64)) missing[ei][si][mi].append(zeros(num, int32)) # Initialise the relaxation dispersion fit functions. model = Dispersion(model=spins[0].model, num_params=param_num(spins=spins), num_spins=len(spins), num_frq=field_count, exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=relax_times, recalc_tau=recalc_tau, r1_fit=r1_fit) # Make a single function call. This will cause back calculation and the data will be stored in the class instance. chi2 = model.func(param_vector) # Store the chi-squared value. if store_chi2: for spin in spins: spin.chi2 = chi2 # Return the structure. return model.get_back_calc()
def __init__(self, exp_type=None, num_spins=1, model=None, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Special method __init__() is called first (acts as Constructor). It brings in data from outside the class like the variable num_spins (in this case num_spins is also set to a default value of 1). The first parameter of any method/function in the class is always self, the name self is used by convention. Assigning num_spins to self.num_spins allows it to be passed to all methods within the class. Think of self as a carrier, or if you want impress folks call it target instance object. @keyword exp_type: The list of experiment types. @type exp_type: list of str @keyword num_spins: Number of spins in the cluster. @type num_spins: integer @keyword model: The dispersion model to instantiate the Dispersion class with. @type model: string @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings """ # Define parameters self.exp_type = exp_type self.model = model self.num_spins = num_spins #self.fields = array([800. * 1E6]) #self.fields = array([600. * 1E6, 800. * 1E6]) self.fields = array([600. * 1E6, 800. * 1E6, 900. * 1E6]) # Set The spin-lock field strength, nu1, in Hz self.spin_lock_fields = [431.0, 651.2, 800.5, 984.0, 1341.11] # Required data structures. self.relax_times = self.fields / (100 * 100. *1E6 ) self.ncycs = [] self.points = [] self.value = [] self.error = [] for i in range(len(self.fields)): ncyc = arange(2, 1000. * self.relax_times[i], 4) #ncyc = arange(2, 42, 2) self.ncycs.append(ncyc) print("sfrq: ", self.fields[i], "number of cpmg frq", len(ncyc), ncyc) # CPMG data. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: cpmg_point = ncyc / self.relax_times[i] self.points.append(list(cpmg_point)) self.value.append([2.0]*len(cpmg_point)) self.error.append([1.0]*len(cpmg_point)) # R1rho data. else: points = self.spin_lock_fields self.points.append(list(points)) self.value.append([2.0]*len(self.spin_lock_fields)) self.error.append([1.0]*len(self.spin_lock_fields)) # Spin lock offsets in ppm. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: self.offsets = [0] else: self.offsets = list(range(10)) # Chemical shift in ppm. self.chemical_shift = 1.0 # Assemble param vector. self.params = self.assemble_param_vector(r2=r2, r2a=r2a, r2b=r2b, phi_ex=phi_ex, phi_ex_B=phi_ex_B, phi_ex_C=phi_ex_C, dw=dw, dw_AB=dw_AB, dw_BC=dw_BC, dwH=dwH, dwH_AB=dwH_AB, dwH_BC=dwH_BC, pA=pA, pB=pB, kex=kex, kex_AB=kex_AB, kex_BC=kex_BC, kex_AC=kex_AC, kB=kB, kC=kC, k_AB=k_AB, tex=tex, spins_params=spins_params) # Make nested list arrays of data. And return them. values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, spin_lock_nu1 = self.return_r2eff_arrays() # The offset and R1 data. chemical_shifts, offsets, tilt_angles, Delta_omega, w_eff = self.return_offset_data() r1 = ones([self.num_spins, self.fields.shape[0]]) # relax version compatibility. self.relax_times_compat = relax_times if version == 'repository checkout' or version_comparison(version, '3.2.3') == 1: self.relax_times_compat = [] for ei in range(len(self.exp_type)): self.relax_times_compat.append([]) for mi in range(len(self.fields)): self.relax_times_compat[ei].append([]) for oi in range(len(self.offsets)): self.relax_times_compat[ei][mi].append([]) for di in range(len(self.points[mi])): self.relax_times_compat[ei][mi][oi].append(self.relax_times.tolist()) # Init the Dispersion class. self.model = Dispersion(model=self.model, num_params=None, num_spins=self.num_spins, num_frq=len(self.fields), exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=self.relax_times_compat, scaling_matrix=None)
class Profile(Dispersion): """Class Profile inherits the Dispersion container class object.""" def __init__(self, exp_type=None, num_spins=1, model=None, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Special method __init__() is called first (acts as Constructor). It brings in data from outside the class like the variable num_spins (in this case num_spins is also set to a default value of 1). The first parameter of any method/function in the class is always self, the name self is used by convention. Assigning num_spins to self.num_spins allows it to be passed to all methods within the class. Think of self as a carrier, or if you want impress folks call it target instance object. @keyword exp_type: The list of experiment types. @type exp_type: list of str @keyword num_spins: Number of spins in the cluster. @type num_spins: integer @keyword model: The dispersion model to instantiate the Dispersion class with. @type model: string @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings """ # Define parameters self.exp_type = exp_type self.model = model self.num_spins = num_spins #self.fields = array([800. * 1E6]) #self.fields = array([600. * 1E6, 800. * 1E6]) self.fields = array([600. * 1E6, 800. * 1E6, 900. * 1E6]) # Set The spin-lock field strength, nu1, in Hz self.spin_lock_fields = [431.0, 651.2, 800.5, 984.0, 1341.11] # Required data structures. self.relax_times = self.fields / (100 * 100. *1E6 ) self.ncycs = [] self.points = [] self.value = [] self.error = [] for i in range(len(self.fields)): ncyc = arange(2, 1000. * self.relax_times[i], 4) #ncyc = arange(2, 42, 2) self.ncycs.append(ncyc) print("sfrq: ", self.fields[i], "number of cpmg frq", len(ncyc), ncyc) # CPMG data. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: cpmg_point = ncyc / self.relax_times[i] self.points.append(list(cpmg_point)) self.value.append([2.0]*len(cpmg_point)) self.error.append([1.0]*len(cpmg_point)) # R1rho data. else: points = self.spin_lock_fields self.points.append(list(points)) self.value.append([2.0]*len(self.spin_lock_fields)) self.error.append([1.0]*len(self.spin_lock_fields)) # Spin lock offsets in ppm. if EXP_TYPE_CPMG_SQ in self.exp_type or EXP_TYPE_CPMG_MQ in self.exp_type: self.offsets = [0] else: self.offsets = list(range(10)) # Chemical shift in ppm. self.chemical_shift = 1.0 # Assemble param vector. self.params = self.assemble_param_vector(r2=r2, r2a=r2a, r2b=r2b, phi_ex=phi_ex, phi_ex_B=phi_ex_B, phi_ex_C=phi_ex_C, dw=dw, dw_AB=dw_AB, dw_BC=dw_BC, dwH=dwH, dwH_AB=dwH_AB, dwH_BC=dwH_BC, pA=pA, pB=pB, kex=kex, kex_AB=kex_AB, kex_BC=kex_BC, kex_AC=kex_AC, kB=kB, kC=kC, k_AB=k_AB, tex=tex, spins_params=spins_params) # Make nested list arrays of data. And return them. values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, spin_lock_nu1 = self.return_r2eff_arrays() # The offset and R1 data. chemical_shifts, offsets, tilt_angles, Delta_omega, w_eff = self.return_offset_data() r1 = ones([self.num_spins, self.fields.shape[0]]) # relax version compatibility. self.relax_times_compat = relax_times if version == 'repository checkout' or version_comparison(version, '3.2.3') == 1: self.relax_times_compat = [] for ei in range(len(self.exp_type)): self.relax_times_compat.append([]) for mi in range(len(self.fields)): self.relax_times_compat[ei].append([]) for oi in range(len(self.offsets)): self.relax_times_compat[ei][mi].append([]) for di in range(len(self.points[mi])): self.relax_times_compat[ei][mi][oi].append(self.relax_times.tolist()) # Init the Dispersion class. self.model = Dispersion(model=self.model, num_params=None, num_spins=self.num_spins, num_frq=len(self.fields), exp_types=exp_types, values=values, errors=errors, missing=missing, frqs=frqs, frqs_H=frqs_H, cpmg_frqs=cpmg_frqs, spin_lock_nu1=spin_lock_nu1, chemical_shifts=chemical_shifts, offset=offsets, tilt_angles=tilt_angles, r1=r1, relax_times=self.relax_times_compat, scaling_matrix=None) def return_offset_data(self): """Return numpy arrays of the chemical shifts, offsets and tilt angles. @keyword field_count: The number of spectrometer field strengths. This may not be equal to the length of the fields list as the user may not have set the field strength. @type field_count: int @keyword fields: The spin-lock field strengths to use instead of the user loaded values - to enable interpolation. The dimensions are {Ei, Mi}. @type fields: rank-2 list of floats """ # spins=[spin], spin_ids=[spin_id], field_count=field_count, fields=spin_lock_nu1 # Initialise the data structures for the target function. shifts = [] offsets = [] theta = [] Domega = [] w_e = [] for ei in range(len(self.exp_type)): shifts.append([]) offsets.append([]) theta.append([]) Domega.append([]) w_e.append([]) for si in range(self.num_spins): shifts[ei].append([]) offsets[ei].append([]) theta[ei].append([]) Domega[ei].append([]) w_e[ei].append([]) for mi in range(len(self.fields)): shifts[ei][si].append(None) offsets[ei][si].append([]) theta[ei][si].append([]) Domega[ei][si].append([]) w_e[ei][si].append([]) for oi in range(len(self.offsets)): offsets[ei][si][mi].append(None) theta[ei][si][mi].append([]) Domega[ei][si][mi].append([]) w_e[ei][si][mi].append([]) # Assemble the data. si = 0 for spin_index in range(self.num_spins): for ei in range(len(self.exp_type)): exp_type = self.exp_type[ei] # Add the experiment type. for mi in range(len(self.fields)): # Get the frq. frq = self.fields[mi] for oi in range(len(self.offsets)): # The spin-lock data. # Convert the shift from ppm to rad/s and store it. shifts[ei][si][mi] = self.chemical_shift * 2.0 * pi * frq / g1H * g15N * 1e-6 # Set The spin-lock offset, omega_rf, in ppm. offset = self.offsets[oi] # Store the offset in rad/s. Only once and using the first key. offsets[ei][si][mi][oi] = offset * 2.0 * pi * frq / g1H * g15N * 1e-6 # Loop over the dispersion points. for di in range(len(self.spin_lock_fields)): # Alias the point. point = self.spin_lock_fields[di] # Skip reference spectra. if point == None: continue # Calculate the tilt angle. omega1 = point * 2.0 * pi Delta_omega = shifts[ei][si][mi] - offsets[ei][si][mi][oi] Domega[ei][si][mi][oi].append(Delta_omega) if Delta_omega == 0.0: theta[ei][si][mi][oi].append(pi / 2.0) # Calculate the theta angle describing the tilted rotating frame relative to the laboratory. # theta = atan(omega1 / Delta_omega). # If Delta_omega is negative, there follow the symmetry of atan, that atan(-x) = - atan(x). # Then it should be: theta = pi + atan(-x) = pi - atan(x) = pi - abs(atan( +/- x)). # This is taken care of with the atan2(y, x) function, which return atan(y / x), in radians, and the result is between -pi and pi. else: theta[ei][si][mi][oi].append(atan2(omega1, Delta_omega)) # Calculate effective field in rotating frame w_eff = sqrt( Delta_omega*Delta_omega + omega1*omega1 ) w_e[ei][si][mi][oi].append(w_eff) # Increment the spin index. si += 1 # Return the structures. return shifts, offsets, theta, Domega, w_e def return_r2eff_arrays(self): """Return numpy arrays of the R2eff/R1rho values and errors. @return: The numpy array structures of the R2eff/R1rho values, errors, missing data, and corresponding Larmor frequencies. For each structure, the first dimension corresponds to the experiment types, the second the spins of a spin block, the third to the spectrometer field strength, and the fourth is the dispersion points. For the Larmor frequency structure, the fourth dimension is omitted. For R1rho-type data, an offset dimension is inserted between the spectrometer field strength and the dispersion points. @rtype: lists of numpy float arrays, lists of numpy float arrays, lists of numpy float arrays, numpy rank-2 int array """ # Initialise the data structures for the target function. exp_types = [] values = [] errors = [] missing = [] frqs = [] frqs_H = [] relax_times = [] offsets = [] for ei in range(len(self.exp_type)): values.append([]) errors.append([]) missing.append([]) frqs.append([]) frqs_H.append([]) relax_times.append([]) offsets.append([]) for si in range(self.num_spins): values[ei].append([]) errors[ei].append([]) missing[ei].append([]) frqs[ei].append([]) frqs_H[ei].append([]) offsets[ei].append([]) for mi in range(len(self.fields)): values[ei][si].append([]) errors[ei][si].append([]) missing[ei][si].append([]) frqs[ei][si].append(0.0) frqs_H[ei][si].append(0.0) offsets[ei][si].append([]) for oi in range(len(self.offsets)): values[ei][si][mi].append([]) errors[ei][si][mi].append([]) missing[ei][si][mi].append([]) offsets[ei][si][mi].append([]) for mi in range(len(self.fields)): relax_times[ei].append(None) cpmg_frqs = [] for ei in range(len(self.exp_type)): cpmg_frqs.append([]) for mi in range(len(self.fields)): cpmg_frqs[ei].append([]) for oi in range(len(self.offsets)): #cpmg_frqs[ei][mi].append(self.points) cpmg_frqs[ei][mi].append([]) spin_lock_nu1 = [] for ei in range(len(self.exp_type)): spin_lock_nu1.append([]) for mi in range(len(self.fields)): spin_lock_nu1[ei].append([]) for oi in range(len(self.offsets)): #cpmg_frqs[ei][mi].append(self.points) spin_lock_nu1[ei][mi].append([]) # Pack the R2eff/R1rho data. si = 0 for spin_index in range(self.num_spins): data_flag = True for ei in range(len(self.exp_type)): exp_type = self.exp_type[ei] # Add the experiment type. if exp_type not in exp_types: exp_types.append(exp_type) for mi in range(len(self.fields)): # Get the frq. frq = self.fields[mi] # The Larmor frequency for this spin (and that of an attached proton for the MMQ models) and field strength (in MHz*2pi to speed up the ppm to rad/s conversion). frqs[ei][si][mi] = 2.0 * pi * frq / g1H * g15N * 1e-6 frqs_H[ei][si][mi] = 2.0 * pi * frq * 1e-6 for oi in range(len(self.offsets)): # CPMG data. if exp_type == EXP_TYPE_CPMG_SQ or EXP_TYPE_CPMG_MQ in self.exp_type: # Get the cpmg frq. cpmg_frqs[ei][mi][oi] = self.points[mi] back_calc = array([0.0]*len(cpmg_frqs[ei][mi][oi])) # R1rho data. else: # Get the spin_lock_nu1 frq. spin_lock_nu1[ei][mi][oi] = self.points[mi] back_calc = array([0.0]*len(spin_lock_nu1[ei][mi][oi])) for di in range(len(self.points[mi])): missing[ei][si][mi][oi].append(0) # Values #values[ei][si][mi][oi].append(self.value[mi][di]) values[ei][si][mi][oi].append(back_calc[di]) # The errors. errors[ei][si][mi][oi].append(self.error[mi][di]) #print self.value[mi][di], self.error[mi][di] # The relaxation times. # Found. relax_time = self.relax_times[mi] # Store the time. relax_times[ei][mi] = relax_time # Increment the spin index. si += 1 # Convert to numpy arrays. relax_times = array(relax_times, float64) for ei in range(len(self.exp_type)): for si in range(self.num_spins): for mi in range(len(self.fields)): for oi in range(len(self.offsets)): cpmg_frqs[ei][mi][oi] = array(cpmg_frqs[ei][mi][oi], float64) spin_lock_nu1[ei][mi][oi] = array(spin_lock_nu1[ei][mi][oi], float64) values[ei][si][mi][oi] = array(values[ei][si][mi][oi], float64) errors[ei][si][mi][oi] = array(errors[ei][si][mi][oi], float64) missing[ei][si][mi][oi] = array(missing[ei][si][mi][oi], int32) # Return the structures. return values, errors, cpmg_frqs, missing, frqs, frqs_H, exp_types, relax_times, offsets, asarray(spin_lock_nu1) def assemble_param_vector(self, r2=None, r2a=None, r2b=None, phi_ex=None, phi_ex_B=None, phi_ex_C=None, dw=None, dw_AB=None, dw_BC=None, dwH=None, dwH_AB=None, dwH_BC=None, pA=None, pB=None, kex=None, kex_AB=None, kex_BC=None, kex_AC=None, kB=None, kC=None, k_AB=None, tex=None, spins_params=None): """Assemble the dispersion relaxation dispersion curve fitting parameter vector. @keyword r2: The transversal relaxation rate. @type r2: float @keyword r2a: The transversal relaxation rate for state A in the absence of exchange. @type r2a: float @keyword r2b: The transversal relaxation rate for state B in the absence of exchange. @type r2b: float @keyword phi_ex: The phi_ex = pA.pB.dw**2 value (ppm^2) @type phi_ex: float @keyword phi_ex_B: The fast exchange factor between sites A and B (ppm^2) @type phi_ex_B: float @keyword phi_ex_C: The fast exchange factor between sites A and C (ppm^2) @type phi_ex_C: float @keyword dw: The chemical exchange difference between states A and B in ppm. @type dw: float @keyword pA: The population of state A. @type pA: float @keyword kex: The rate of exchange. @type kex: float @keyword kB : The rate of exchange. @type kB: float @keyword kC: The rate of exchange. @type kC: float @keyword k_AB: The exchange rate from state A to state B @type k_AB: float @keyword tex: The exchange time. @type tex: float @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings @return: An array of the parameter values of the dispersion relaxation model. @rtype: numpy float array """ # Initialise. param_vector = [] # Loop over the parameters of the cluster. for param_name, spin_index, mi in self.loop_parameters(spins_params=spins_params): if param_name == 'r2': value = r2 value = value + mi + spin_index*0.1 elif param_name == 'r2a': value = r2a value = value + mi+ spin_index*0.1 elif param_name == 'r2b': value = r2b value = value + mi + spin_index*0.1 elif param_name == 'phi_ex': value = phi_ex + spin_index elif param_name == 'phi_ex_B': value = phi_ex_B + spin_index elif param_name == 'phi_ex_C': value = phi_ex_C + spin_index elif param_name == 'dw': value = dw + spin_index elif param_name == 'dw_AB': value = dw_AB + spin_index elif param_name == 'dw_BC': value = dw_BC + spin_index elif param_name == 'dwH': value = dwH + spin_index elif param_name == 'dwH_AB': value = dw_AB + spin_index elif param_name == 'dwH_BC': value = dw_BC + spin_index elif param_name == 'pA': value = pA elif param_name == 'pB': value = pB elif param_name == 'kex': value = kex elif param_name == 'kex_AB': value = kex_AB elif param_name == 'kex_BC': value = kex_BC elif param_name == 'kex_AC': value = kex_AC elif param_name == 'kB': value = kB elif param_name == 'kC': value = kC elif param_name == 'k_AB': value = k_AB elif param_name == 'tex': value = tex # Add to the vector. param_vector.append(value) # Return a numpy array. return array(param_vector, float64) def loop_parameters(self, spins_params=None): """Generator function for looping of the parameters of the cluster. @keyword spins_params: List of parameter strings used in dispersion model. @type spins_params: array of strings @return: The parameter name. @rtype: str """ # Loop over the parameters of the cluster. # First the R2 parameters (one per spin per field strength). for spin_index in range(self.num_spins): # The R2 parameter. if 'r2' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2', spin_index, mi # The R2A parameter. if 'r2a' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2a', spin_index, mi # The R2B parameter. if 'r2b' in spins_params: for ei in range(len(self.exp_type)): for mi in range(len(self.fields)): yield 'r2b', spin_index, mi # Then the chemical shift difference parameters 'phi_ex', 'phi_ex_B', 'phi_ex_C', 'padw2', 'dw', 'dw_AB', 'dw_BC', 'dw_AB' (one per spin). for spin_index in range(self.num_spins): # Yield the data. if 'phi_ex' in spins_params: yield 'phi_ex', spin_index, 0 if 'phi_ex_B' in spins_params: yield 'phi_ex_B', spin_index, 0 if 'phi_ex_C' in spins_params: yield 'phi_ex_C', spin_index, 0 if 'padw2' in spins_params: yield 'padw2', pspin_index, 0 if 'dw' in spins_params: yield 'dw', spin_index, 0 if 'dw_AB' in spins_params: yield 'dw_AB', spin_index, 0 if 'dw_BC' in spins_params: yield 'dw_BC', spin_index, 0 # Then a separate block for the proton chemical shift difference parameters for the MQ models (one per spin). for spin_index in range(self.num_spins): if 'dwH' in spins_params: yield 'dwH', spin_index, 0 if 'dwH_AB' in spins_params: yield 'dwH_AB', spin_index, 0 if 'dwH_BC' in spins_params: yield 'dwH_BC', spin_index, 0 # All other parameters (one per spin cluster). for param in spins_params: if not param in ['r2', 'r2a', 'r2b', 'phi_ex', 'phi_ex_B', 'phi_ex_C', 'padw2', 'dw', 'dw_AB', 'dw_BC', 'dw_AB', 'dwH', 'dwH_AB', 'dwH_BC', 'dwH_AB']: if param == 'pA': yield 'pA', 0, 0 elif param == 'pB': yield 'pB', 0, 0 elif param == 'kex': yield 'kex', 0, 0 elif param == 'kex_AB': yield 'kex_AB', 0, 0 elif param == 'kex_BC': yield 'kex_BC', 0, 0 elif param == 'kex_AC': yield 'kex_AC', 0, 0 elif param == 'kB': yield 'kB', 0, 0 elif param == 'kC': yield 'kC', 0, 0 elif param == 'k_AB': yield 'k_AB', 0, 0 elif param == 'tex': yield 'tex', 0, 0 def calc(self, params): """Calculate chi2 values. @keyword params: List of parameter strings used in dispersion model. @type params: array of strings @return: Chi2 value. @rtype: float """ # Return chi2 value. chi2 = self.model.func(params) return chi2