/* fwdmodel_cest_devel.cc - Developement CEST APT model Michael Chappell, IBME PUMMA & FMRIB Image Analysis Group Copyright (C) 2010 University of Oxford */ /* Part of FSL - FMRIB's Software Library http://www.fmrib.ox.ac.uk/fsl fsl@fmrib.ox.ac.uk Developed at FMRIB (Oxford Centre for Functional Magnetic Resonance Imaging of the Brain), Department of Clinical Neurology, Oxford University, Oxford, UK LICENCE FMRIB Software Library, Release 5.0 (c) 2012, The University of Oxford (the "Software") The Software remains the property of the University of Oxford ("the University"). The Software is distributed "AS IS" under this Licence solely for non-commercial use in the hope that it will be useful, but in order that the University as a charitable foundation protects its assets for the benefit of its educational and research purposes, the University makes clear that no condition is made or to be implied, nor is any warranty given or to be implied, as to the accuracy of the Software, or that it will be suitable for any particular purpose or for use under any specific conditions. Furthermore, the University disclaims all responsibility for the use which is made of the Software. It further disclaims any liability for the outcomes arising from using the Software. The Licensee agrees to indemnify the University and hold the University harmless from and against any and all claims, damages and liabilities asserted by third parties (including claims for negligence) which arise directly or indirectly from the use of the Software or the sale of any products based on the Software. No part of the Software may be reproduced, modified, transmitted or transferred in any form or by any means, electronic or mechanical, without the express permission of the University. The permission of the University is not required if the said reproduction, modification, transmission or transference is done without financial return, the conditions of this Licence are imposed upon the receiver of the product, and all original and amended source code is included in any transmitted product. You may be held legally responsible for any copyright infringement that is caused or encouraged by your failure to abide by these terms and conditions. You are not permitted under this Licence to use this Software commercially. Use for which any financial return is received shall be defined as commercial use, and includes (1) integration of all or part of the source code or the Software into a product for sale or license by or on behalf of Licensee to third parties or (2) use of the Software or any derivative of it for research with the final aim of developing software products for sale or license to a third party or (3) use of the Software or any derivative of it for research with the final aim of developing non-software products for sale or license to a third party, or (4) use of the Software to provide any service to an external organisation for which payment is received. If you are interested in using the Software commercially, please contact Isis Innovation Limited ("Isis"), the technology transfer company of the University, to negotiate a licence. Contact details are: innovation@isis.ox.ac.uk quoting reference DE/9564. */ #include "fwdmodel_cest_devel.h" #include #include #include #include "newimage/newimageall.h" #include "miscmaths/miscprob.h" using namespace NEWIMAGE; #include "easylog.h" string CESTDevelFwdModel::ModelVersion() const { return "$Id: fwdmodel_cest_devel.cc,v 1.4 2010/10/01 13:46:16 chappell Exp $"; } void CESTDevelFwdModel::HardcodedInitialDists(MVNDist& prior, MVNDist& posterior) const { Tracer_Plus tr("CESTDevelFwdModel::HardcodedInitialDists"); assert(prior.means.Nrows() == NumParams()); SymmetricMatrix precisions = IdentityMatrix(NumParams()) * 1e-12; // Set priors // M0 prior.means(1) = 0.0; prior.means(2) = 0.0; //0.001 prior.means(3) = 0.0; //0.1; precisions(1,1) = 1e-12; precisions(2,2) = 1e6; //1e7 precisions(3,3) = 1e99; //2500; !OFF if (mton) precisions(3,3) = 2500; // exchnage consts (these are log_e) prior.means(4) = 3; //1.3; precisions(4,4) = 1; prior.means(5) = 3.7;//1.6; precisions(5,5) = 10; // frequency offsets (ppm) prior.means(6) = 0; precisions(6,6) = 100; prior.means(7) = ppm_apt_set; //3.5; precisions(7,7) = 100; prior.means(8) = -2.34; precisions(8,8) = 1e99; // !OFF if (mton) precisions(8,8) = 100; // B1 offset (fractional) prior.means(9) = 0; precisions(9,9) = 1e14; int idx = 10; //should be next entry in priors if (pvcorr) { // M0 prior.means(idx) = 0.0; prior.means(idx+1) = 0.0; prior.means(idx+2) = 0.15; precisions(idx,idx) = 1e-12; precisions(idx+1,idx+1) = 100; precisions(idx+2,idx+2) = 2500; // exchnage consts (these are log_e) prior.means(idx+3) = 3; precisions(idx+3,idx+3) = 1; prior.means(idx+4) = 3.7; precisions(idx+4,idx+4) = 1; prior.means(idx+5) = 0.0; precisions(idx+5,idx+5) = 1e-12; prior.means(idx+6) = 1.0; precisions(idx+6,idx+6) = 1e12; prior.means(idx+7) = 0.0; precisions(idx+7,idx+7) = 1e12; prior.means(idx+8) = 0.0; precisions(idx+8,idx+8) = 1e12; idx += 9; } //T1 and T2 values int t12idx=idx; if (t12soft) { for (int i=0; i 0.1) { //dont expect large ratios M0ratio = 0.1; } M0(j) = M0ratio*M0(1); place++; } // now exchange - we assume that only significant exchnage occurs with water kij=0.0; //float ktemp; for (int j=2; j<=npool; j++) { //ktemp = exp(params(place)); //this is the 'fundamental rate const - non-linear transformation //kij(j,1) = ktemp*M0(1); //kij(1,j) = ktemp*M0(j); kij(j,1) = exp(params(place)); //non-linear transformation kij(1,j) =kij(j,1)*M0(j)/M0(1); place++; } // frequency offset next float ppm_off = params(place); //(params.Rows(place,place)).AsScalar(); // frequncy offset due to field place++; float ppm_apt = params(place); place++; float ppm_mt = params(place); place++; // now B1 offset float B1off = params(place)*1e6; //scale the B1_off parameter to achieve proper updating of this parameter // (otherwise it is sufficently small that numerical diff is ineffective) place++; // PV correction section (untested) ColumnVector M0_WM(npool); Matrix kij_WM(npool,npool); Matrix T12_WM(2,npool); Matrix T12_CSF(2,1); Matrix M0_CSF(1,1); float PV_GM; float PV_WM; float PV_CSF; if (pvcorr) { // now parameters for WM M0_WM(1) = paramcpy(place); // this is the M0 value of water if (M0_WM(1)<1e-4) M0_WM(1)=1e-4; //M0 of water cannot disapear all together place++; // this code here if parameters contains actual M0 values //M0.Rows(2,npool) = paramcpy.Rows(place,place+npool-1-1); // place +=npool-1; // values in the parameters are ratios of M0_water float M0WMratio; for (int j=2; j<=npool; j++) { M0WMratio = paramcpy(place); if (M0WMratio > 0.1) { //dont expect large ratios M0WMratio = 0.1; } M0_WM(j) = M0WMratio*M0_WM(1); place++; } // now exchange - we assume that only significant exchnage occurs with water kij_WM=0.0; //float ktemp; for (int j=2; j<=npool; j++) { //ktemp = exp(params(place)); //this is the 'fundamental rate const - non-linear transformation //kij(j,1) = ktemp*M0(1); //kij(1,j) = ktemp*M0(j); kij_WM(j,1) = exp(params(place)); //non-linear transformation kij_WM(1,j) =kij_WM(j,1)*M0_WM(j)/M0_WM(1); place++; } // CSF M0_CSF = paramcpy(place); place++; //partial volume estimates PV_GM = paramcpy(place); place++; PV_WM = paramcpy(place); place++; PV_CSF = paramcpy(place); place++; //T12 fixed T12_WM = T12WMmaster; T12_CSF = T12CSFmaster; } // T12 parameter values if (t12soft) { // T12 values for (int i=1; i<=npool; i++) { T12(1,i) = paramcpy(place); if (T12(1,i)<1e-12) T12(1,i)=1e-12; // 0 is no good for a T1 value if (T12(1,i)>10) T12(1,i)=10; // Prevent convergence issues causing T1 to blow up place++; } for (int i=1; i<=npool; i++) { T12(2,i) = paramcpy(place); if (T12(2,i)<1e-12) T12(2,i)=1e-12; //0 is no good for a T2 value if (T12(2,i)>1) T12(2,i)=1; // Prevent convergence issues causing T2 to blow up place++; } } else {T12 = T12master;} //WASSR image M0 float M0_WASSR=0.0; if (wassron & !wassronly) { M0_WASSR = paramcpy(place); place++; } else { M0_WASSR = (M0.Row(1)).AsScalar(); //if we are doing wassr only then use the main M0 value if (wassron) place++; } //cout << "Parameters set up" << endl; //cout << "M0: " << M0.t() << endl << "wi: " << wi.t() << endl << "kij: " << kij << endl; //MODEL - CEST ColumnVector cest_result; //no saturation image first ColumnVector nosat(1); //cest_result.ReSize(1); if (pvcorr) { nosat = PV_GM*M0.Row(1) + PV_WM*M0_WM.Row(1) + PV_CSF*M0_CSF.Row(1);} else { nosat = M0.Row(1);} if (!wassronly) { //iterate over the data sets for (unsigned int n=0; n10) B1off=10; //unlikely to get this big (hardlimit in case of convergence problems) float B1 = B1set[n] * (1+B1off); float w1 = 42.58e6*B1*2*M_PI; // in radians! ColumnVector thisresult; if (pvcorr) { ColumnVector GM_result; GM_result = Mz_spectrum(wvec[n],w1,t[n],M0,wi,kij,T12); ColumnVector WM_result; WM_result = Mz_spectrum(wvec[n],w1,t[n],M0_WM,wi,kij_WM,T12_WM); ColumnVector CSF_result; CSF_result = Mz_spectrum(wvec[n],w1,t[n],M0_CSF,wi.Row(1),kij.SubMatrix(1,1,1,1),T12_CSF); thisresult = PV_GM*GM_result + PV_WM*WM_result + PV_CSF*CSF_result; } else { thisresult = Mz_spectrum(wvec[n],w1,t[n],M0,wi,kij,T12); } cest_result &= thisresult; /* if (n>0) { cest_result = cest_result & thisresult; } else { cest_result = thisresult; } */ } cest_result &= nosat; } //cout << cest_result.t() << endl; //MODEL - WASSR ColumnVector wassr_result; //no saturation image first ColumnVector wassr_nosat(1); //wassr_result.ReSize(1); wassr_nosat = M0_WASSR; if (wassron) { //wassr_result = Mz_spectrum(W_wvec,W_w1,W_t,M0,wi,kij,T12); //NB this line uses the main CEST wi not specific to the WASSR acquisition centre frequency Matrix W_wlocal(1,1); ColumnVector M0W(1); M0W = M0_WASSR; W_wlocal = W_wlam*ppm_off/1e6; wassr_result &= Mz_spectrum(W_wvec,W_w1,W_t,M0W,W_wlocal,kij.SubMatrix(1,1,1,1),T12.Column(1)); //use simple 1-pool model for the wassr spectrum } wassr_result &= wassr_nosat; if (wassronly) { result = wassr_result; } else { if(wassron) { result = cest_result & wassr_result;} else { result = cest_result; } } return; } CESTDevelFwdModel::CESTDevelFwdModel(ArgsType& args) { Tracer_Plus tr("CESTDevelFwdModel"); string scanParams = args.ReadWithDefault("scan-params","cmdline"); wassron=false; t12soft=false; pvcorr=false;basic=false;mton=false; // define input parameters that we need here vector wrange; vector wstart; vector wstep; vector nfreq; //WASSR input parameters float W_B1=0.0; float W_wrange; int W_nfreq; vector wcentre; float W_wcentre=-1.0; bool revfrq; if (scanParams == "cmdline") { //primary dataset string tsat_text = args.ReadWithDefault("tsat","none!"); if (tsat_text != "none!") { B1set.push_back( convertTo(args.ReadWithDefault("B1","0.0")) ); t.push_back( convertTo(tsat_text) ); wrange.push_back( convertTo(args.ReadWithDefault("wrange","0.0")) ); wstart.push_back( convertTo(args.ReadWithDefault("wstart","0.0")) ); wstep.push_back( convertTo(args.ReadWithDefault("wstep","0.0")) ); nfreq.push_back( convertTo(args.ReadWithDefault("nfreq","0")) ); wcentre.push_back( convertTo(args.ReadWithDefault("wcentre","-1.0")) ); } //additional datasets int N=1; while (true) { tsat_text = args.ReadWithDefault("tsat_"+stringify(N),"stop!"); //cout << tsat_text << endl; if (tsat_text == "stop!") break; //there are no more datasets defined //append new dataset parameters t.push_back( convertTo(tsat_text) ); B1set.push_back( convertTo(args.ReadWithDefault("B1_"+stringify(N),"0.0")) ); wrange.push_back( convertTo(args.ReadWithDefault("wrange_"+stringify(N),"0.0")) ); wstart.push_back( convertTo(args.ReadWithDefault("wstart_"+stringify(N),"0.0")) ); wstep.push_back( convertTo(args.ReadWithDefault("wstep_"+stringify(N),"0.0")) ); nfreq.push_back( convertTo(args.ReadWithDefault("nfreq_"+stringify(N),"0")) ); wcentre.push_back( convertTo(args.ReadWithDefault("wcentre_"+stringify(N),"-1.0")) ); N++; } W_B1 = convertTo(args.ReadWithDefault("W_B1","0.0")); W_t = convertTo(args.ReadWithDefault("W_tsat","0.0")); W_wrange = convertTo(args.ReadWithDefault("W_wrange","0.0")); W_nfreq = convertTo(args.ReadWithDefault("W_nfreq","0")); W_wcentre = convertTo(args.ReadWithDefault("W_wcentre","-1.0")); wassronly = args.ReadBool("wassronly"); basic = args.ReadBool("basic"); pvcorr = args.ReadBool("pvcorr"); t12soft = args.ReadBool("t12prior"); mton = args.ReadBool("infermt"); ppm_apt_set = convertTo(args.ReadWithDefault("ppm_apt","3.5")); revfrq=args.ReadBool("reverse"); } else throw invalid_argument("Only --scan-params=cmdline is accepted at the moment"); cout << "No. CEST datasets: " << t.size() << endl; //for (unsigned int n=0; n1e-12) { wassron=true; //cout << "WASSR is on" << endl; } //initialization npool = 3; // some fixed things T12master.ReSize(2,npool); T12master << 1.3 << 0.77 << 1 << 0.05 << 0.01 << 1e-5; //T2 T12WMmaster.ReSize(2,npool); T12WMmaster << 1 << 0.77 << 1 << 0.02 << 0.01 << 1e-5; //T2 T12CSFmaster.ReSize(2,1); T12CSFmaster << 3.5 << .75; //ppm_apt = -3.5; //ppm_mt = -2.41; //deal with water centre frequency *in radians!* float wdefault = 42.58e6*3*2*M_PI; // this is the default values (3T) if (W_wcentre>0) W_wlam = W_wcentre*1e6*2*M_PI; //NB input is in MHz else W_wlam = wdefault; wlam.resize(t.size()); for (unsigned int n=0; n0) wlam[n] = wcentre[n]*1e6*2*M_PI; //NB input is in MHz else wlam[n] = wdefault; // CEST frequency vector //int nfreq=32; ColumnVector ppmvec(nfreq[n]); float ppminc; if (wrange[n] > 0.0) { //deafult case is that wrange is specified ppmvec(1) = -wrange[n]; ppminc = (2*wrange[n])/(nfreq[n]-1); } else { //if wrange is zero then we use wstart and wstep parameters ppmvec(1) = wstart[n]; ppminc = wstep[n]; } //cout << ppminc << endl; for (int i=2; i<=nfreq[n]; i++) { ppmvec(i) = ppmvec(i-1) + ppminc; } //wvec.ReSize(nfreq); ColumnVector freqvec(nfreq[n]); freqvec = ppmvec*wlam[n]/1e6; if (revfrq) freqvec = -freqvec; //cout << freqvec.t() << endl; wvec.push_back( freqvec ); //cout << wvec[n] << endl; //cout << wlam[n] << endl; } if (wassron) { //float W_B1 = 0.4e-6; W_w1 = 42.58e6*W_B1*2*M_PI; // in radians! // WASSR frequency vector ColumnVector ppmvec(W_nfreq); ppmvec(1) = -W_wrange; float ppminc = (2*W_wrange)/(W_nfreq-1); //cout << ppminc << endl; for (int i=2; i<=W_nfreq; i++) { ppmvec(i) = ppmvec(i-1) + ppminc; } W_wvec.ReSize(W_nfreq); W_wvec = ppmvec*W_wlam/1e6; } } void CESTDevelFwdModel::ModelUsage() { cout << "\nUsage info for --model=CEST:\n" << "Undefined\n" ; } void CESTDevelFwdModel::DumpParameters(const ColumnVector& vec, const string& indent) const { } void CESTDevelFwdModel::NameParams(vector& names) const { names.clear(); names.push_back("M0a"); names.push_back("M0b_r"); names.push_back("M0c_r"); names.push_back("kba"); names.push_back("kca"); names.push_back("ppm_off"); names.push_back("ppm_apt"); names.push_back("ppm_mt"); names.push_back("B1_off"); if (pvcorr) { names.push_back("WM_M0a"); names.push_back("WM_M0b_r"); names.push_back("WM_M0c_r"); names.push_back("WM_kba"); names.push_back("WM_kca"); names.push_back("CSF_M0a"); } if (t12soft) { names.push_back("T1a"); names.push_back("T1b"); names.push_back("T1c"); names.push_back("T2a"); names.push_back("T2b"); names.push_back("T2c"); } if (wassron) { names.push_back("M0_WASSR"); } } void CESTDevelFwdModel::SetupARD( const MVNDist& theta, MVNDist& thetaPrior, double& Fard) { Tracer_Plus tr("CESTDevelFwdModel::SetupARD"); if (doard) { //sort out ARD indices Fard = 0; int ardindex; for (unsigned int i=0; i Apowers; //cout << coeff << endl; switch (m) { case 3: case 5: case 7: case 9: //cout << "case " << m << endl; Apowers.push_back(I); Apowers.push_back(inmatrix*inmatrix); for (int j=3; j<=ceil((m+1/2)); j++) { Apowers.push_back(Apowers[j-2]*Apowers[1]); } U=0.0; V=0.0; //cout << "Apowers" <=2; j -= 2) { U += coeff(j)*Apowers[j/2-1]; } U = inmatrix*U; for (int j=m; j>=1; j -= 2) { V += coeff(j)*Apowers[(j+1)/2-1]; } //cout << U << endl << V << endl; X = (U+V)*(-U+V).i(); //cout << X<< endl; break; case 13: //cout << "case 13" << endl; Matrix A2(n,n); Matrix A4(n,n); Matrix A6(n,n); A2 = inmatrix*inmatrix; A4 = A2*A2; A6 = A2*A4; U = inmatrix * (A6*(coeff(14)*A6 + coeff(12)*A4 + coeff(10)*A2) + coeff(8)*A6 + coeff(6)*A4 + coeff(4)*A2 + coeff(2)*I ); //cout << U; V = A6*(coeff(13)*A6 + coeff(11)*A4 + coeff(9)*A2) + coeff(7)*A6 + coeff(5)*A4 + coeff(3)*A2 + coeff(1)*I; //cout << V; X = (U+V)*(-U+V).i(); //cout << X <