nmrrelax.cpp
1 /***************************************************************************
2  Copyright (C) 2002-2015 Kentaro Kitagawa
3  kitagawa@phys.s.u-tokyo.ac.jp
4 
5  This program is free software; you can redistribute it and/or
6  modify it under the terms of the GNU Library General Public
7  License as published by the Free Software Foundation; either
8  version 2 of the License, or (at your option) any later version.
9 
10  You should have received a copy of the GNU Library General
11  Public License and a list of authors along with this program;
12  see the files COPYING and AUTHORS.
13 ***************************************************************************/
14 #include "nmrrelax.h"
15 #include "nmrrelaxfit.h"
16 #include "ui_nmrrelaxform.h"
17 #include "nmrpulse.h"
18 #include "pulserdriver.h"
19 #include "analyzer.h"
20 #include "graph.h"
21 #include "graphwidget.h"
22 #include "rand.h"
23 
24 REGISTER_TYPE(XDriverList, NMRT1, "NMR relaxation measurement");
25 
26 #include <QPushButton>
27 #include <QComboBox>
28 #include <QCheckBox>
29 
30 const char XNMRT1::P1DIST_LINEAR[] = "Linear";
31 const char XNMRT1::P1DIST_LOG[] = "Log";
32 const char XNMRT1::P1DIST_RECIPROCAL[] = "Reciprocal";
33 
34 const char XNMRT1::P1STRATEGY_RANDOM[] = "Random";
35 const char XNMRT1::P1STRATEGY_FLATTEN[] = "Flatten";
36 
37 
38 class XRelaxFuncPlot : public XFuncPlot {
39 public:
40  XRelaxFuncPlot(const char *name, bool runtime, Transaction &tr, const shared_ptr<XGraph> &graph
41  , const shared_ptr<XItemNode < XRelaxFuncList, XRelaxFunc > > &item,
42  const shared_ptr<XNMRT1> &owner)
43  : XFuncPlot(name, runtime, tr, graph), m_item(item), m_owner(owner)
44  {}
45  ~XRelaxFuncPlot() {}
46  virtual double func(double t) const {
47  shared_ptr<XNMRT1> owner = m_owner.lock();
48  if( !owner) return 0;
49  Snapshot shot( *owner);
50  shared_ptr<XRelaxFunc> func1 = shot[ *m_item];
51  if( !func1) return 0;
52  double f, df;
53  double it1 = shot[ *owner].m_params[0];
54  double c = shot[ *owner].m_params[1];
55  double a = shot[ *owner].m_params[2];
56  func1->relax( &f, &df, t, it1);
57  return c * f + a;
58  }
59 private:
60  shared_ptr<XItemNode < XRelaxFuncList, XRelaxFunc > > m_item;
61  weak_ptr<XNMRT1> m_owner;
62 };
63 
64 XNMRT1::XNMRT1(const char *name, bool runtime,
65  Transaction &tr_meas, const shared_ptr<XMeasure> &meas)
66  : XSecondaryDriver(name, runtime, ref(tr_meas), meas),
67  m_relaxFuncs(create<XRelaxFuncList>("RelaxFuncs", true)),
68  m_t1inv(create<XScalarEntry>("T1inv", false,
69  dynamic_pointer_cast<XDriver>(shared_from_this()))),
70  m_t1invErr(create<XScalarEntry>("T1invErr", false,
71  dynamic_pointer_cast<XDriver>(shared_from_this()))),
72  m_pulser(create<XItemNode < XDriverList, XPulser > >(
73  "Pulser", false, ref(tr_meas), meas->drivers(), true)),
74  m_pulse1(create<XItemNode < XDriverList, XNMRPulseAnalyzer > >(
75  "NMRPulseAnalyzer1", false, ref(tr_meas), meas->drivers(), true)),
76  m_pulse2(create<XItemNode < XDriverList, XNMRPulseAnalyzer > >(
77  "NMRPulseAnalyzer2", false, ref(tr_meas), meas->drivers())),
78  m_active(create<XBoolNode>("Active", false)),
79  m_autoPhase(create<XBoolNode>("AutoPhase", false)),
80  m_mInftyFit(create<XBoolNode>("MInftyFit", false)),
81  m_absFit(create<XBoolNode>("AbsFit", false)),
82  m_trackPeak(create<XBoolNode>("TrackPeakFreq", false)),
83  m_p1Min(create<XDoubleNode>("P1Min", false)),
84  m_p1Max(create<XDoubleNode>("P1Max", false)),
85  m_p1Next(create<XDoubleNode>("P1Next", true)),
86  m_p1AltNext(create<XDoubleNode>("P1Next", true)),
87  m_phase(create<XDoubleNode>("Phase", false, "%.2f")),
88  m_freq(create<XDoubleNode>("Freq", false, "%.3f")),
89  m_windowFunc(create<XComboNode>("WindowFunc", false, true)),
90  m_autoWindow(create<XBoolNode>("AutoWindow", false)),
91  m_windowWidth(create<XComboNode>("WindowWidth", false, true)),
92  m_mode(create<XComboNode>("Mode", false, true)),
93  m_smoothSamples(create<XUIntNode>("SmoothSamples", false)),
94  m_p1Strategy(create<XComboNode>("P1Strategy", false, true)),
95  m_p1Dist(create<XComboNode>("P1Dist", false, true)),
96  m_resetFit(create<XTouchableNode>("ResetFit", true)),
97  m_clearAll(create<XTouchableNode>("ClearAll", true)),
98  m_fitStatus(create<XStringNode>("FitStatus", true)),
99  m_solver(create<SpectrumSolverWrapper>("SpectrumSolver", true, shared_ptr<XComboNode>(), m_windowFunc, shared_ptr<XDoubleNode>())),
100  m_form(new FrmNMRT1),
101  m_statusPrinter(XStatusPrinter::create(m_form.get())),
102  m_wave(create<XWaveNGraph>("Wave", true, m_form->m_graph, m_form->m_edDump, m_form->m_tbDump, m_form->m_btnDump)) {
103 
104  iterate_commit([=](Transaction &tr){
105  m_relaxFunc = create<XItemNode < XRelaxFuncList, XRelaxFunc > >(
106  tr, "RelaxFunc", false, ref(tr), m_relaxFuncs, true);
107  });
108 
109  m_form->m_btnClear->setIcon(QApplication::style()->standardIcon(QStyle::SP_DialogResetButton));
110  m_form->m_btnResetFit->setIcon(QApplication::style()->standardIcon(QStyle::SP_BrowserReload));
111 
112  m_form->setWindowTitle(i18n("NMR Relaxation Measurement - ") + getLabel() );
113 
114  meas->scalarEntries()->insert(tr_meas, t1inv());
115  meas->scalarEntries()->insert(tr_meas, t1invErr());
116 
117  connect(pulser());
118  connect(pulse1());
119  connect(pulse2());
120 
121  iterate_commit([=](Transaction &tr){
122  tr[ *m_windowFunc] = SpectrumSolverWrapper::WINDOW_FUNC_HAMMING;
123  m_windowWidthList = {0.25, 0.5, 1.0, 1.5, 2.0};
124  tr[ *m_windowWidth].add({"25%", "50%", "100%", "150%", "200%"});
125  tr[ *m_windowWidth] = 2;
126 
127  const char *labels[] = {"P1 [ms] or 2Tau [us]", "Intens [V]",
128  "Weight [1/V]", "Abs [V]", "Re [V]", "Im [V]"};
129  tr[ *m_wave].setColCount(6, labels);
130  tr[ *m_wave].insertPlot(i18n("Relaxation"), 0, 1, -1, 2);
131  tr[ *m_wave].insertPlot(i18n("Out-of-Phase"), 0, 5, -1, 2);
132  shared_ptr<XAxis> axisx = tr[ *m_wave].axisx();
133  shared_ptr<XAxis> axisy = tr[ *m_wave].axisy();
134  tr[ *axisx->logScale()] = true;
135  tr[ *axisy->label()] = i18n("Relaxation");
136  tr[ *tr[ *m_wave].plot(0)->drawLines()] = false;
137  tr[ *tr[ *m_wave].plot(1)->drawLines()] = false;
138  tr[ *tr[ *m_wave].plot(1)->intensity()] = 0.6;
139  shared_ptr<XFuncPlot> plot3 = m_wave->graph()->plots()->create<XRelaxFuncPlot>(
140  tr, "FittedCurve", true, ref(tr), m_wave->graph(),
141  relaxFunc(), static_pointer_cast<XNMRT1>(shared_from_this()));
142  tr[ *plot3->label()] = i18n("Fitted Curve");
143  tr[ *plot3->axisX()] = axisx;
144  tr[ *plot3->axisY()] = axisy;
145  tr[ *plot3->drawPoints()] = false;
146  tr[ *plot3->pointColor()] = clBlue;
147  tr[ *plot3->lineColor()] = clBlue;
148  tr[ *plot3->drawBars()].setUIEnabled(false);
149  tr[ *plot3->barColor()].setUIEnabled(false);
150  tr[ *plot3->clearPoints()].setUIEnabled(false);
151  tr[ *m_wave].clearPoints();
152 
153  tr[ *mode()].add({"T1 Measurement", "T2 Measurement", "St.E. Measurement"});
154  tr[ *mode()] = MEAS_T1;
155 
156  tr[ *p1Strategy()].add({P1STRATEGY_RANDOM, P1STRATEGY_FLATTEN});
157  tr[ *p1Strategy()] = 1;
158 
159  tr[ *p1Dist()].add({P1DIST_LINEAR, P1DIST_LOG, P1DIST_RECIPROCAL});
160  tr[ *p1Dist()] = 1;
161 
162  tr[ *relaxFunc()].str(XString("NMR I=1/2"));
163  tr[ *p1Min()] = 1.0;
164  tr[ *p1Max()] = 100.0;
165  tr[ *autoPhase()] = true;
166  tr[ *autoWindow()] = true;
167  tr[ *mInftyFit()] = true;
168  tr[ *trackPeak()] = false;
169  tr[ *smoothSamples()] = 40;
170  });
171 
172  m_form->m_dblPhase->setRange( -360.0, 360.0);
173  m_form->m_dblPhase->setSingleStep(10.0);
174 
175  m_conUIs = {
176  xqcon_create<XQLineEditConnector>(m_p1Min, m_form->m_edP1Min),
177  xqcon_create<XQLineEditConnector>(m_p1Max, m_form->m_edP1Max),
178  xqcon_create<XQLineEditConnector>(m_p1Next, m_form->m_edP1Next),
179  xqcon_create<XQDoubleSpinBoxConnector>(m_phase, m_form->m_dblPhase, m_form->m_slPhase),
180  xqcon_create<XQLineEditConnector>(m_freq, m_form->m_edFreq),
181  xqcon_create<XQToggleButtonConnector>(m_autoWindow, m_form->m_ckbAutoWindow),
182  xqcon_create<XQComboBoxConnector>(m_windowFunc, m_form->m_cmbWindowFunc, Snapshot( *m_windowFunc)),
183  xqcon_create<XQComboBoxConnector>(m_windowWidth, m_form->m_cmbWindowWidth, Snapshot( *m_windowWidth)),
184  xqcon_create<XQLineEditConnector>(m_smoothSamples, m_form->m_edSmoothSamples),
185  xqcon_create<XQComboBoxConnector>(m_p1Strategy, m_form->m_cmbP1Strategy, Snapshot( *m_p1Strategy)),
186  xqcon_create<XQComboBoxConnector>(m_p1Dist, m_form->m_cmbP1Dist, Snapshot( *m_p1Dist)),
187  xqcon_create<XQButtonConnector>(m_clearAll, m_form->m_btnClear),
188  xqcon_create<XQButtonConnector>(m_resetFit, m_form->m_btnResetFit),
189  xqcon_create<XQToggleButtonConnector>(m_active, m_form->m_ckbActive),
190  xqcon_create<XQToggleButtonConnector>(m_autoPhase, m_form->m_ckbAutoPhase),
191  xqcon_create<XQToggleButtonConnector>(m_mInftyFit, m_form->m_ckbMInftyFit),
192  xqcon_create<XQToggleButtonConnector>(m_absFit, m_form->m_ckbAbsFit),
193  xqcon_create<XQToggleButtonConnector>(m_trackPeak, m_form->m_ckbTrackPeak),
194  xqcon_create<XQTextBrowserConnector>(m_fitStatus, m_form->m_txtFitStatus),
195  xqcon_create<XQComboBoxConnector>(m_relaxFunc, m_form->m_cmbRelaxFunc, Snapshot( *m_relaxFuncs)),
196  xqcon_create<XQComboBoxConnector>(m_mode, m_form->m_cmbMode, Snapshot( *m_mode)),
197  xqcon_create<XQComboBoxConnector>(m_pulser, m_form->m_cmbPulser, ref(tr_meas)),
198  xqcon_create<XQComboBoxConnector>(m_pulse1, m_form->m_cmbPulse1, ref(tr_meas)),
199  xqcon_create<XQComboBoxConnector>(m_pulse2, m_form->m_cmbPulse2, ref(tr_meas))
200  };
201 
202  iterate_commit([=](Transaction &tr){
203  m_lsnOnActiveChanged = tr[ *active()].onValueChanged().connectWeakly(
204  shared_from_this(), &XNMRT1::onActiveChanged);
205  m_lsnOnP1CondChanged = tr[ *p1Max()].onValueChanged().connectWeakly(
206  shared_from_this(), &XNMRT1::onP1CondChanged);
207  for(auto &&x: std::vector<shared_ptr<XValueNodeBase>>(
208  {p1Min(), p1Strategy(), p1Dist(), smoothSamples()}))
209  tr[ *x].onValueChanged().connect(m_lsnOnP1CondChanged);
210  m_lsnOnCondChanged = tr[ *phase()].onValueChanged().connectWeakly(
211  shared_from_this(), &XNMRT1::onCondChanged);
212  for(auto &&x: std::vector<shared_ptr<XValueNodeBase>>(
213  {mInftyFit(), absFit(), relaxFunc(), autoPhase(), freq(), autoWindow(),
214  windowFunc(), windowWidth(), mode()}))
215  tr[ *x].onValueChanged().connect(m_lsnOnCondChanged);
216 
217  m_lsnOnClearAll = tr[ *m_clearAll].onTouch().connectWeakly(
218  shared_from_this(), &XNMRT1::onClearAll);
219  m_lsnOnResetFit = tr[ *m_resetFit].onTouch().connectWeakly(
220  shared_from_this(), &XNMRT1::onResetFit);
221  });
222 }
223 void
225  m_form->showNormal();
226  m_form->raise();
227 }
228 
229 void
230 XNMRT1::onClearAll(const Snapshot &shot, XTouchableNode *) {
231  trans( *this).m_timeClearRequested = XTime::now();
232  requestAnalysis();
233 }
234 double
235 XNMRT1::distributeP1(const Snapshot &shot, double uniform_x_0_to_1) {
236  double p1min = shot[ *p1Min()];
237  double p1max = shot[ *p1Max()];
238  double p1;
239  if(shot[ *p1Dist()].to_str() == P1DIST_LINEAR)
240  p1 = (1-(uniform_x_0_to_1)) * p1min + (uniform_x_0_to_1) * p1max;
241  else if(shot[ *p1Dist()].to_str() == P1DIST_LOG)
242  p1 = p1min * exp((uniform_x_0_to_1) * log(p1max/p1min));
243  else
244  //P1DIST_RECIPROCAL
245  p1 =1/((1-uniform_x_0_to_1)/p1min + (uniform_x_0_to_1)/p1max);
246 
247  p1 = llrint(p1 * 1e4) / 1e4;//rounds.
248  return p1;
249 }
250 void
251 XNMRT1::onResetFit(const Snapshot &shot, XTouchableNode *) {
252  iterate_commit([=](Transaction &tr){
253  const Snapshot &shot(tr);
254  double x = randMT19937();
255  double p1min = shot[ *p1Min()];
256  double p1max = shot[ *p1Max()];
257  if((p1min <= 0) || (p1min >= p1max)) {
258  gErrPrint(i18n("Invalid P1Min or P1Max."));
259  return;
260  }
261  tr[ *this].m_params[0] = 1.0 / distributeP1(shot, x);
262  tr[ *this].m_params[1] = 0.1;
263  tr[ *this].m_params[2] = 0.0;
264  });
265  requestAnalysis();
266 }
267 void
268 XNMRT1::obtainNextP1(Transaction &tr) {
269  const Snapshot &shot(tr);
270  double x_0_to_1;
271  if(shot[ *p1Strategy()] .to_str() == P1STRATEGY_RANDOM) {
272  x_0_to_1 = randMT19937();
273  }
274  else {
275  //FLATTEN
276  int samples = shot[ *this].m_sumpts.size();
277  if( !samples) {
278  x_0_to_1 = 0.5;
279  }
280  else {
281  //binary search for area having small sum isigma.
282  double p1min = shot[ *p1Min()];
283  double p1max = shot[ *p1Max()];
284  int lb = 0, ub = samples;
285  double k_0 = samples / log(p1max/p1min);
286  int idx_p1next = lrint(log(shot[ *p1Next()] / p1min) * k_0);
287  idx_p1next = std::min(std::max(idx_p1next, 0), samples);
288  bool p1dist_linear = (shot[ *p1Dist()].to_str() == P1DIST_LINEAR);
289  bool p1dist_log = (shot[ *p1Dist()].to_str() == P1DIST_LOG);
290  const auto &sumpts = shot[ *this].m_sumpts;
291  for(;;) {
292  int mid;
293  if(p1dist_log)
294  mid = (lb + ub) / 2; //m_sumpts has been stored in log scale.
295  else {
296  double xlb = exp(lb / k_0) * p1min;
297  double xub = exp(ub / k_0) * p1min;
298  double xhalf;
299  if(p1dist_linear)
300  xhalf = (xlb + xub) / 2;
301  else
302  xhalf = 1.0/((1/xlb + 1/xub) / 2); //reciprocal
303  mid = lrint(log(xhalf / p1min) * k_0);
304  }
305  assert((mid >= lb) && (mid <= ub));
306  int isigma_0 = 0;
307  for(int idx = lb; idx < mid; ++idx)
308  isigma_0 += sumpts[idx].isigma;
309  int isigma_1 = 0;
310  for(int idx = mid; idx < ub; ++idx)
311  isigma_1 += sumpts[idx].isigma;
312  if((lb <= idx_p1next) && (mid > idx_p1next))
313  isigma_0++;
314  if((mid <= idx_p1next) && (ub > idx_p1next))
315  isigma_1++;
316  if(isigma_0 == isigma_1) {
317  if(randMT19937() < 0.5)
318  ub = mid;
319  else
320  lb = mid;
321  }
322  else {
323  if(isigma_0 < isigma_1)
324  ub = mid;
325  else
326  lb = mid;
327  }
328  if(ub - lb <= 1) {
329  x_0_to_1 = (double)lb / samples;
330  break;
331  }
332  }
333  }
334  }
335  tr[ *p1Next()] = distributeP1(shot, x_0_to_1);
336  tr[ *p1AltNext()] = distributeP1(shot, 1 - x_0_to_1);
337 }
338 void
339 XNMRT1::onP1CondChanged(const Snapshot &shot, XValueNodeBase *node) {
340  requestAnalysis();
341  iterate_commit([=](Transaction &tr){
342  const Snapshot &shot(tr);
343  double p1min = shot[ *p1Min()];
344  double p1max = shot[ *p1Max()];
345  if((p1min <= 0) || (p1min >= p1max)) {
346  gErrPrint(i18n("Invalid P1Min or P1Max."));
347  return;
348  }
349  obtainNextP1(tr);
350  });
351 }
352 void
353 XNMRT1::onCondChanged(const Snapshot &shot, XValueNodeBase *node) {
354 // if((node == phase()) && *autoPhase()) return;
355 // if(((node == windowWidth()) || (node == windowFunc())) && *autoWindow()) return;
356  if(
357  (node == mode().get()) ||
358  (node == freq().get())) {
359  trans( *this).m_timeClearRequested = XTime::now();
360  }
361  requestAnalysis();
362 }
363 void
364 XNMRT1::analyzeSpectrum(Transaction &tr,
365  const std::vector< std::complex<double> >&wave, int origin, double cf,
366  std::deque<std::complex<double> > &value_by_cond) {
367  const Snapshot &shot_this(tr);
368 
369  value_by_cond.clear();
370  std::deque<FFT::twindowfunc> funcs;
371  m_solver->windowFuncs(funcs);
372 
373  int idx = 0;
374  for(std::deque<double>::const_iterator wit = m_windowWidthList.begin(); wit != m_windowWidthList.end(); wit++) {
375  for(std::deque<FFT::twindowfunc>::iterator fit = funcs.begin(); fit != funcs.end(); fit++) {
376  if(shot_this[ *this].m_convolutionCache.size() <= idx) {
377  tr[ *this].m_convolutionCache.push_back(
378  std::make_shared<Payload::ConvolutionCache>());
379  }
380  shared_ptr<Payload::ConvolutionCache> cache = tr[ *this].m_convolutionCache[idx];
381  if((cache->windowwidth != *wit) || (cache->origin != origin) ||
382  (cache->windowfunc != *fit) || (cache->cfreq != cf) ||
383  (cache->wave.size() != wave.size())) {
384  cache->windowwidth = *wit;
385  cache->origin = origin;
386  cache->windowfunc = *fit;
387  cache->cfreq = cf;
388  cache->power = 0.0;
389  cache->wave.resize(wave.size());
390  double wk = 1.0 / FFTSolver::windowLength(wave.size(), -origin, *wit);
391  for(int i = 0; i < (int)wave.size(); i++) {
392  double w = ( *fit)((i - origin) * wk) / (double)wave.size();
393  cache->wave[i] = std::polar(w, -2.0*M_PI*cf*(i - origin));
394  cache->power += w*w;
395  }
396  }
397 
398  std::complex<double> z(0.0);
399  for(int i = 0; i < (int)cache->wave.size(); i++) {
400  z += wave[i] * cache->wave[i];
401  }
402 
403 // m_solver->solver()->exec(wave, fftout, -origin, 0.0, *fit, *wit);
404 // value_by_cond.push_back(fftout[(cf + fftlen) % fftlen]);
405  value_by_cond.push_back(z);
406  idx++;
407  }
408  }
409 }
410 bool
412  const Snapshot &shot_emitter, const Snapshot &shot_others,
413  XDriver *emitter) const {
414  shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
415  shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
416  shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
417  if( !pulser__ || !pulse1__) return false;
418  if(emitter == this) return true;
419  if(emitter == pulser__.get()) return false;
420  assert((emitter == pulse1__.get()) || (emitter == pulse2__.get()));
421 
422  const Snapshot &shot_pulse1((emitter == pulse1__.get()) ? shot_emitter : shot_others);
423  const Snapshot &shot_pulse2((emitter == pulse2__.get()) ? shot_emitter : shot_others);
424 
425  if(shot_others[ *pulser__].time() > shot_pulse1[ *pulse1__].time()) return false;
426 
427  switch(shot_others[ *pulser__].combMode()) {
428  default:
429  if(emitter == pulse2__.get())
430  return false;
431  return true;
432  case XPulser::N_COMB_MODE_COMB_ALT:
433  case XPulser::N_COMB_MODE_P1_ALT:
434  if( !pulse2__) {
435  m_statusPrinter->printError(i18n("2 Pulse Analyzers needed."));
436  return false;
437  }
438  if(shot_pulse1[ *pulse1__].time() != shot_pulse2[ *pulse2__].time()) return false;
439  return true;
440  }
441 // return (pulser__->time() < pulse1__->timeAwared()) && (pulser__->time() < pulse1__->time());
442 }
443 
444 void
445 XNMRT1::analyze(Transaction &tr, const Snapshot &shot_emitter, const Snapshot &shot_others,
446  XDriver *emitter) throw (XRecordError&) {
447  Snapshot &shot_this(tr);
448 
449  double p1min = shot_this[ *p1Min()];
450  double p1max = shot_this[ *p1Max()];
451 
452  if((p1min <= 0) || (p1min >= p1max)) {
453  throw XRecordError(i18n("Invalid P1Min or P1Max."), __FILE__, __LINE__);
454  }
455 
456  int samples = shot_this[ *smoothSamples()];
457  if(samples <= 10) {
458  throw XRecordError(i18n("Invalid # of Samples."), __FILE__, __LINE__);
459  }
460  if(samples >= 100000) {
461  m_statusPrinter->printWarning(i18n("Too many Samples."), true);
462  }
463 
464  int mode__ = shot_this[ *mode()];
465  shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
466  shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
467  const Snapshot &shot_pulse1((emitter == pulse1__.get()) ? shot_emitter : shot_others);
468  const Snapshot &shot_pulse2((emitter == pulse2__.get()) ? shot_emitter : shot_others);
469 
470  shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
471  const Snapshot &shot_pulser(shot_others);
472  assert( pulser__ );
473  if(shot_pulser[ *pulser__].time()) {
474  //Check consitency.
475  switch (mode__) {
476  case MEAS_T1:
477  break;
478  case MEAS_T2:
479  break;
480  case MEAS_ST_E:
481  if((shot_pulser[ *pulser__].tau() != shot_pulser[ *pulser__].combPT()) ||
482  (shot_pulser[ *pulser__].combNum() != 2) ||
483  ( !shot_pulser[ *pulser__->conserveStEPhase()]) ||
484  (shot_pulser[ *pulser__].pw1() != 0.0) ||
485  (shot_pulser[ *pulser__].pw2() != shot_pulser[ *pulser__].combPW()))
486  m_statusPrinter->printWarning(i18n("Strange St.E. settings."));
487  break;
488  }
489  }
490 
491  //Reads spectra from NMRPulseAnalyzers
492  if( emitter != this) {
493  assert( pulse1__ );
494  assert( shot_pulse1[ *pulse1__].time() );
495  assert( shot_pulser[ *pulser__].time() );
496  assert( emitter != pulser__.get() );
497 
498  if(shot_pulse1[ *pulse1__->exAvgIncr()]) {
499  m_statusPrinter->printWarning(i18n("Do NOT use incremental avg. Skipping."));
500  throw XSkippedRecordError(__FILE__, __LINE__);
501  }
502 
503  std::deque<std::complex<double> > cmp1, cmp2;
504  double cfreq = shot_this[ *freq()] * 1e3 * shot_pulse1[ *pulse1__].interval();
505  if(shot_this[ *trackPeak()]) {
506  if(((mode__ == MEAS_T1) && (shot_pulser[ *pulser__].combP1() > distributeP1(shot_this, 0.66))) ||
507  ((mode__ == MEAS_T2) && (shot_pulser[ *pulser__].combP1() < distributeP1(shot_this, 0.33))) ||
508  shot_this[ *this].m_sumpts.empty()) {
509  tr[ *freq()] = (double)shot_pulse1[ *pulse1__->entryPeakFreq()->value()];
510  tr.unmark(m_lsnOnCondChanged); //avoiding recursive signaling.
511  }
512  }
513 
514  analyzeSpectrum(tr, shot_pulse1[ *pulse1__].wave(),
515  shot_pulse1[ *pulse1__].waveFTPos(), cfreq, cmp1);
516  Payload::RawPt pt1, pt2;
517  if(pulse2__) {
518  analyzeSpectrum(tr, shot_pulse2[ *pulse2__].wave(),
519  shot_pulse2[ *pulse2__].waveFTPos(), cfreq, cmp2);
520  pt2.value_by_cond.resize(cmp2.size());
521  }
522  pt1.value_by_cond.resize(cmp1.size());
523  switch(shot_pulser[ *pulser__].combMode()) {
524  default:
525  throw XRecordError(i18n("Unknown Comb Mode!"), __FILE__, __LINE__);
526  case XPulser::N_COMB_MODE_COMB_ALT:
527  if(mode__ != MEAS_T1) throw XRecordError(i18n("Use T1 mode!"), __FILE__, __LINE__);
528  assert(pulse2__);
529  pt1.p1 = shot_pulser[ *pulser__].combP1();
530  for(int i = 0; i < cmp1.size(); i++)
531  pt1.value_by_cond[i] = (cmp1[i] - cmp2[i]) / cmp1[i];
532  tr[ *this].m_pts.push_back(pt1);
533  break;
534  case XPulser::N_COMB_MODE_P1_ALT:
535  if(mode__ == MEAS_T2)
536  throw XRecordError(i18n("Do not use T2 mode!"), __FILE__, __LINE__);
537  assert(pulse2__);
538  pt1.p1 = shot_pulser[ *pulser__].combP1();
539  std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
540  tr[ *this].m_pts.push_back(pt1);
541  pt2.p1 = shot_pulser[ *pulser__].combP1Alt();
542  std::copy(cmp2.begin(), cmp2.end(), pt2.value_by_cond.begin());
543  tr[ *this].m_pts.push_back(pt2);
544  break;
545  case XPulser::N_COMB_MODE_ON:
546  if(mode__ != MEAS_T2) {
547  pt1.p1 = shot_pulser[ *pulser__].combP1();
548  std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
549  tr[ *this].m_pts.push_back(pt1);
550  break;
551  }
552  m_statusPrinter->printWarning(i18n("T2 mode with comb pulse!"));
553  case XPulser::N_COMB_MODE_OFF:
554  if(mode__ != MEAS_T2) {
555  m_statusPrinter->printWarning(i18n("Do not use T1 mode! Skipping."));
556  throw XSkippedRecordError(__FILE__, __LINE__);
557  }
558  //T2 measurement
559  pt1.p1 = 2.0 * shot_pulser[ *pulser__].tau();
560  std::copy(cmp1.begin(), cmp1.end(), pt1.value_by_cond.begin());
561  tr[ *this].m_pts.push_back(pt1);
562  break;
563  }
564  }
565 
566  tr[ *this].m_sumpts.clear();
567 
568  if(shot_this[ *this].m_timeClearRequested > shot_pulse1[ *pulse1__].timeAwared()) {
569  tr[ *this].m_pts.clear();
570  tr[ *m_wave].clearPoints();
571  tr[ *m_fitStatus] = "";
572  trans( *pulse1__->avgClear()).touch();
573  if(pulse2__)
574  trans( *pulse2__->avgClear()).touch();
575  throw XSkippedRecordError(__FILE__, __LINE__);
576  }
577 
578  shared_ptr<XRelaxFunc> func = shot_this[ *relaxFunc()];
579  if( !func) {
580  throw XRecordError(i18n("Please select relaxation func."), __FILE__, __LINE__);
581  }
582 
583  tr[ *this].m_sumpts.resize(samples);
584  auto &sumpts(tr[ *this].m_sumpts);
585 
586  {
587  Payload::Pt dummy;
588  dummy.c = 0; dummy.p1 = 0; dummy.isigma = 0;
589  dummy.value_by_cond.resize(shot_this[ *this].m_convolutionCache.size());
590  std::fill(tr[ *this].m_sumpts.begin(), tr[ *this].m_sumpts.end(), dummy);
591  double k = shot_this[ *this].m_sumpts.size() / log(p1max/p1min);
592  auto pts_begin(shot_this[ *this].m_pts.begin());
593  auto pts_end(shot_this[ *this].m_pts.end());
594  int sum_size = (int)shot_this[ *this].m_sumpts.size();
595  for(auto it = pts_begin; it != pts_end; it++) {
596  int idx = lrint(log(it->p1 / p1min) * k);
597  if((idx < 0) || (idx >= sum_size)) continue;
598  double p1 = it->p1;
599  //For St.E., T+tau = P1+3*tau.
600  if(mode__ == MEAS_ST_E)
601  p1 += 3 * shot_pulser[ *pulser__].tau() * 1e-3;
602  sumpts[idx].isigma += 1;
603  sumpts[idx].p1 += p1;
604  for(unsigned int i = 0; i < it->value_by_cond.size(); i++)
605  sumpts[idx].value_by_cond[i] += it->value_by_cond[i];
606  }
607  }
608 
609  std::deque<std::complex<double> > sum_c(
610  shot_this[ *this].m_convolutionCache.size()), corr(shot_this[ *this].m_convolutionCache.size());
611  double sum_t = 0.0;
612  int n = 0;
613  for(auto it = shot_this[ *this].m_sumpts.begin();
614  it != shot_this[ *this].m_sumpts.end(); it++) {
615  if(it->isigma == 0) continue;
616  double t = log10(it->p1 / it->isigma);
617  for(unsigned int i = 0; i < it->value_by_cond.size(); i++) {
618  sum_c[i] += it->value_by_cond[i];
619  corr[i] += it->value_by_cond[i] * t;
620  }
621  sum_t += t * it->isigma;
622  n += it->isigma;
623  }
624  if(n) {
625  //correlation for y_i * log(t_i)
626  for(unsigned int i = 0; i < corr.size(); i++) {
627  corr[i] -= sum_c[i]*sum_t/(double)n;
628  corr[i] *= ((mode__ == MEAS_T1) ? 1 : -1);
629  }
630 
631  bool absfit__ = shot_this[ *absFit()];
632 
633  std::deque<double> phase_by_cond(corr.size(), shot_this[ *phase()] / 180.0 * M_PI);
634  int cond = -1;
635  double maxsn2 = 0.0;
636  for(unsigned int i = 0; i < corr.size(); i++) {
637  if(shot_this[ *autoPhase()]) {
638  phase_by_cond[i] = std::arg(corr[i]);
639  }
640  if(shot_this[ *autoWindow()]) {
641  double sn2 = absfit__ ? std::abs(corr[i]) : std::real(corr[i] * std::polar(1.0, -phase_by_cond[i]));
642  sn2 = sn2 * sn2 / shot_this[ *this].m_convolutionCache[i]->power;
643  if(maxsn2 < sn2) {
644  maxsn2 = sn2;
645  cond = i;
646  }
647  }
648  }
649  if(cond < 0) {
650  cond = 0;
651  for(std::deque<shared_ptr<Payload::ConvolutionCache> >::const_iterator it
652  = shot_this[ *this].m_convolutionCache.begin();
653  it != shot_this[ *this].m_convolutionCache.end(); ++it) {
654  if((m_windowWidthList[std::max(0, (int)shot_this[ *windowWidth()])] == ( *it)->windowwidth) &&
655  (m_solver->windowFunc(shot_this) == ( *it)->windowfunc)) {
656  break;
657  }
658  cond++;
659  }
660  }
661  if(cond >= (shot_this[ *this].m_convolutionCache.size())) {
662  throw XSkippedRecordError(__FILE__, __LINE__);
663  }
664  double ph = phase_by_cond[cond];
665  if(shot_this[ *autoPhase()]) {
666  tr[ *phase()] = ph / M_PI * 180;
667  tr.unmark(m_lsnOnCondChanged); //avoiding recursive signaling.
668  }
669  if(shot_this[ *autoWindow()]) {
670  for(unsigned int i = 0; i < m_windowWidthList.size(); i++) {
671  if(m_windowWidthList[i] == shot_this[ *this].m_convolutionCache[cond]->windowwidth)
672  tr[ *windowWidth()] = i;
673  }
674  std::deque<FFT::twindowfunc> funcs;
675  m_solver->windowFuncs(funcs);
676  for(unsigned int i = 0; i < funcs.size(); i++) {
677  if(funcs[i] == shot_this[ *this].m_convolutionCache[cond]->windowfunc)
678  tr[ *windowFunc()] = i;
679  }
680  tr.unmark(m_lsnOnCondChanged); //avoiding recursive signaling.
681  }
682  std::complex<double> cph(std::polar(1.0, -phase_by_cond[cond]));
683  for(auto it = sumpts.begin(); it != sumpts.end(); it++) {
684  if(it->isigma == 0) continue;
685  it->p1 = it->p1 / it->isigma;
686  it->c = it->value_by_cond[cond] * cph / (double)it->isigma;
687  it->var = (absfit__) ? std::abs(it->c) : std::real(it->c);
688  it->isigma = sqrt(it->isigma);
689  }
690 
691  tr[ *m_fitStatus] = iterate(tr, func, 4);
692 
693  t1inv()->value(tr, 1000.0 * shot_this[ *this].m_params[0]);
694  t1invErr()->value(tr, 1000.0 * shot_this[ *this].m_errors[0]);
695  }
696 
697  m_isPulserControlRequested = (emitter != this);
698 }
699 void
700 XNMRT1::setNextP1(const Snapshot &shot) {
701  shared_ptr<XPulser> pulser__ = shot[ *pulser()];
702  if(pulser__) {
703  pulser__->iterate_commit([=](Transaction &tr){
704  switch(shot[ *mode()]) {
705  case MEAS_T1:
706  case MEAS_ST_E:
707  tr[ *pulser__->combP1()] = (double)shot[ *p1Next()];
708  tr[ *pulser__->combP1Alt()] = (double)shot[ *p1AltNext()];
709  break;
710  case MEAS_T2:
711  tr[ *pulser__->tau()] = shot[ *p1Next()] / 2.0;
712  break;
713  }
714  });
715  iterate_commit([=](Transaction &tr){
716  obtainNextP1(tr);
717  });
718  }
719 }
720 void
722  if( !shot[ *this].time()) {
723  iterate_commit([=](Transaction &tr){
724  tr[ *m_wave].clearPoints();
725  });
726  return;
727  }
728 
729  //set new P1s
730  if(shot[ *active()] && m_isPulserControlRequested.compare_set_strong((int)true, (int)false)) {
731  setNextP1(shot);
732  }
733 
734  m_wave->iterate_commit([=](Transaction &tr){
735  XString label;
736  switch(shot[ *mode()]) {
737  case MEAS_T1:
738  label = "P1 [ms]";
739  break;
740  case MEAS_T2:
741  label = "2tau [us]";
742  break;
743  case MEAS_ST_E:
744  label = "T+tau [ms]";
745  break;
746  }
747  tr[ *m_wave].setLabel(0, label.c_str());
748  tr[ *tr[ *m_wave].axisx()->label()] = label;
749  tr[ *m_wave].setRowCount(shot[ *this].m_sumpts.size());
750  double *colp1 = tr[ *m_wave].cols(0);
751  double *colval = tr[ *m_wave].cols(1);
752  double *colabs = tr[ *m_wave].cols(3);
753  double *colre = tr[ *m_wave].cols(4);
754  double *colim = tr[ *m_wave].cols(5);
755  double *colisigma = tr[ *m_wave].cols(2);
756  int i = 0;
757  for(auto it = shot[ *this].m_sumpts.begin();
758  it != shot[ *this].m_sumpts.end(); it++) {
759  if(it->isigma == 0) {
760  colval[i] = 0;
761  colabs[i] = 0;
762  colre[i] = 0;
763  colim[i] = 0;
764  colp1[i] = 0;
765  }
766  else {
767  colval[i] = it->var;
768  colabs[i] = std::abs(it->c);
769  colre[i] = std::real(it->c);
770  colim[i] = std::imag(it->c);
771  colp1[i] = it->p1;
772  }
773  colisigma[i] = it->isigma;
774  i++;
775  }
776  m_wave->drawGraph(tr);
777  });
778 }
779 
780 void
781 XNMRT1::onActiveChanged(const Snapshot &shot, XValueNodeBase *) {
782  Snapshot shot_this( *this);
783  if(shot_this[ *active()]) {
784  const shared_ptr<XPulser> pulser__ = shot_this[ *pulser()];
785  const shared_ptr<XNMRPulseAnalyzer> pulse1__ = shot_this[ *pulse1()];
786  const shared_ptr<XNMRPulseAnalyzer> pulse2__ = shot_this[ *pulse2()];
787 
788  onClearAll(shot_this, m_clearAll.get());
789  if( !pulser__ || !pulse1__) {
790  gErrPrint(getLabel() + ": " + i18n("No pulser or No NMR Pulse Analyzer."));
791  return;
792  }
793 
794  Snapshot shot_pulse1( *pulse1__);
795  Snapshot shot_pulser( *pulser__);
796  if(pulse2__ &&
797  ((shot_pulser[ *pulser__->combMode()] == XPulser::N_COMB_MODE_COMB_ALT) ||
798  (shot_pulser[ *pulser__->combMode()] == XPulser::N_COMB_MODE_P1_ALT))) {
799  pulse2__->iterate_commit([=](Transaction &tr){
800  tr[ *pulse2__->fromTrig()] =
801  shot_pulse1[ *pulse1__->fromTrig()] + shot_pulser[ *pulser__->altSep()];
802  tr[ *pulse2__->width()] = (double)shot_pulse1[ *pulse1__->width()];
803  tr[ *pulse2__->phaseAdv()] = (double)shot_pulse1[ *pulse1__->phaseAdv()];
804  tr[ *pulse2__->bgPos()] =
805  shot_pulse1[ *pulse1__->bgPos()] + shot_pulser[ *pulser__->altSep()];
806  tr[ *pulse2__->bgWidth()] = (double)shot_pulse1[ *pulse1__->bgWidth()];
807  tr[ *pulse2__->fftPos()] =
808  shot_pulse1[ *pulse1__->fftPos()] + shot_pulser[ *pulser__->altSep()];
809  tr[ *pulse2__->fftLen()] = (int)shot_pulse1[ *pulse1__->fftLen()];
810  tr[ *pulse2__->windowFunc()] = (int)shot_pulse1[ *pulse1__->windowFunc()];
811  tr[ *pulse2__->usePNR()] = (bool)shot_pulse1[ *pulse1__->usePNR()];
812  tr[ *pulse2__->pnrSolverList()] = (int)shot_pulse1[ *pulse1__->pnrSolverList()];
813  tr[ *pulse2__->solverList()] = (int)shot_pulse1[ *pulse1__->solverList()];
814  tr[ *pulse2__->numEcho()] = (int)shot_pulse1[ *pulse1__->numEcho()];
815  tr[ *pulse2__->echoPeriod()] = (double)shot_pulse1[ *pulse1__->echoPeriod()];
816  });
817  }
818  setNextP1(shot_this);
819  }
820 }
821 
822 

Generated for KAME4 by  doxygen 1.8.3