def params_from_experiment(can_params_file, params_cfg): ''' Generates a simulation-compatible configuration given a Cantilever Parameters file (typically acquired in the experiment) and a Params.cfg file saved with FFtrEFM data can_params : string Path to cantilever parameters file (from Force Calibration tab) params_cfg : string Path to parameters.cfg file (from FFtrEFM experiment, in the data folder) ''' can = cantilever_params(can_params_file) _, par = configuration(params_cfg) if isinstance(params_cfg, dict): par = params_cfg can_params = {} can_params['amp_invols'] = can['Initial']['AMPINVOLS'] can_params['def_invols'] = can['Initial']['DEFINVOLS'] can_params['soft_amp'] = 0.3 can_params['drive_freq'] = par['drive_freq'] can_params['res_freq'] = par['drive_freq'] can_params['k'] = can['Initial']['SpringConstant'] can_params['q_factor'] = can['Initial']['Q'] force_params = {} force_params['es_force'] = can['Differential']['ElectroForce'] force_params['ac_force'] = can['Initial']['DrivingForce'] force_params['dc_force'] = 0 # only for GKPFM force_params['delta_freq'] = can['Differential']['ResFrequency'] force_params['tau'] = 1e-5 force_params['dFdz'] = can['Differential']['dFdZ'] force_params['lift_height'] = can['Initial']['LiftHeight'] sim_params = {} sim_params['trigger'] = par['trigger'] sim_params['total_time'] = par['total_time'] sim_params['sampling_rate'] = par['sampling_rate'] return can_params, force_params, sim_params, can, par
def generate_tf(self, can_params_dict={}, plot=False): """ Uses the cantilever simulation to generate a tune as the transfer function :param can_params_dict: use ffta.pixel_utils.load.cantilever_params() :type can_params_dict: Dict :param plot: Plots the time-dependent tune :type plot: bool """ if isinstance(can_params_dict, str): can_params_dict = cantilever_params(can_params_dict) can_params_dict = can_params_dict['Initial'] can_params = { 'amp_invols': 7.5e-08, 'def_invols': 6.88e-08, 'soft_amp': 0.3, 'drive_freq': 309412.0, 'res_freq': 309412.0, 'k': 43.1, 'q_factor': 340.0 } force_params = { 'es_force': 1e-10, 'ac_force': 6e-07, 'dc_force': 3e-09, 'delta_freq': -170.0, 'tau': 0.001, 'v_dc': 3.0, 'v_ac': 2.0, 'v_cpd': 1.0, 'dCdz': 1e-10, 'v_step': 1.0 } sim_params = {'trigger': 0.02, 'total_time': 0.05} for k, v in can_params_dict.items(): can_params.update(k=v) # Update from GKPixel class sim_params['trigger'] = self.trigger sim_params['sampling_rate'] = self.sampling_rate sim_params['total_time'] = self.total_time sim_params['sampling_rate'] = self.sampling_rate can_params['drive_freq'] = self.drive_freq can_params['res_freq'] = self.drive_freq force_keys = ['es_force'] can_keys = { 'amp_invols': ['amp_invols', 'AMPINVOLS'], 'q': ['q_factor', 'Q'], 'k': ['SpringConstant', 'k'] } for f in force_keys: if 'Force' in can_params_dict: force_params.update(es_force=can_params_dict['Force']) elif 'es_force' in can_params_dict: force_params.update(es_force=can_params_dict['es_force']) for c in ['amp_invols', 'q', 'k']: for l in can_keys[c]: if l in can_params_dict: can_params.update(l=can_params_dict[l]) if can_params['k'] < 1e-3: can_params['k'] *= 1e9 # old code had this off by 1e9 cant = Cantilever(can_params, force_params, sim_params) cant.trigger = cant.total_time # don't want a trigger Z, _ = cant.simulate() Z = Z.flatten() if plot: plt.figure() plt.plot(Z) plt.title('Tip response)') TF = np.fft.fftshift(np.fft.fft(Z)) Q = can_params['q_factor'] mid = int(len(self.f_ax) / 2) drive_bin = np.searchsorted(self.f_ax[mid:], self.drive_freq) + mid TFmax = np.abs(TF[drive_bin]) TF_norm = Q * (TF - np.min(np.abs(TF))) / (TFmax - np.min(np.abs(TF))) self.tf = Z self.TF = TF self.TF_norm = TF_norm self.tf_f_ax = np.linspace(-self.sampling_rate / 2, self.sampling_rate / 2, num=self.tf.shape[0]) self.tf_exc = gen_chirp(sampling_rate=self.sampling_rate, length=self.tf.shape / self.sampling_rate) self.TF_EXC = np.fft.fftshift(np.fft.fft(self.tf_exc)) return