// Declarations of classes that implements a hirearchy // of models for long-time constant fields from eddy // currents induced by diffusion gradients. // // LongECModels.h // // Jesper Andersson, FMRIB Image Analysis Group // // Copyright (C) 2022 University of Oxford // #include #include #include #include #include #include #include #include "nlohmann/json.hpp" #include "armawrap/newmat.h" #ifndef EXPOSE_TREACHEROUS #define EXPOSE_TREACHEROUS // To allow us to use .set_sform etc #endif #include "newimage/newimageall.h" #include "miscmaths/miscmaths.h" #include "EddyHelperClasses.h" #include "LongECModels.h" #include "ECScanClasses.h" using namespace EDDY; // Initial value for eddy current time constant (half-life in seconds). // small enough to result in little time dependence, but large enough to have a derivative double const TC_INITIAL_VALUE = 0.1; double const INTCPT_INITIAL_VALUE = 0.01; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class EC_TimeAndIntercept2Weights // // This class translates an EC time constant and intercept to weights // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ arma::colvec EC_TimeAndIntercept2Weights::GetWeightsForPrevious(double Thl, double Intcpt) const EddyTry { arma::colvec weights(_mbg.NGroups()); for (int i=0; icalculate_weights(Thl,prev,curr); return(prev); } EddyCatch arma::colvec EC_TimeConst2Weights::GetWeightsForCurrent(double Thl) const EddyTry { arma::colvec prev(_mbg.NGroups()); arma::colvec curr(_mbg.NGroups()); this->calculate_weights(Thl,prev,curr); return(curr); } EddyCatch void EC_TimeConst2Weights::calculate_weights(double Thl, arma::colvec& prev, arma::colvec& curr) const EddyTry { arma::colvec gtimes(_mbg.NGroups()); // Times at which diffusion gradients are played out arma::colvec otimes(2*_mbg.NGroups()); // Times at which eddy currents are observed arma::colvec all(2*_mbg.NGroups(),0.0); // The effects at all times in order [curr prev] for (int i=0; i 1e-6) { // Only count contribution from gradients in the past break; } all(i) += std::exp(-(otimes(i)-gtimes(j))*std::log(2.0)/Thl); } } // Normalise the weights so that they are 1 for the end of current volume auto nf = all(_mbg.NGroups()-1); std::transform(all.begin(),all.end(),all.begin(),[nf](auto& c){return(c/nf);}); // Finally divide it up into prev and curr curr = all.head_rows(_mbg.NGroups()); prev = all.tail_rows(_mbg.NGroups()); return; } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class IndividualWeightsModel // // This class models the weights directly on a volume-by-volume // basis. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IndividualWeightsModel::IndividualWeightsModel(ECScanManager& sm) EddyTry : _nscans(sm.NScans()), _ngroups(sm.MultiBand().NGroups()), _prev(sm.NScans(),arma::colvec(sm.MultiBand().NGroups(),arma::fill::zeros)), _curr(sm.NScans(),arma::colvec(sm.MultiBand().NGroups(),arma::fill::ones)), _ll(sm.NScans(),1e-6) // Start with very little regularisation { if (!sm.IsDiffusion()) throw EddyException("IndividualWeightsModel::IndividualWeightsModel: sm is not diffusion"); for (unsigned int s=0; s= _nscans) throw EddyException("IndividualWeightsModel::GetDerivParam: s is out of range"); if (i >= NDerivs()) throw EddyException("IndividualWeightsModel::GetDerivParam: i is out of range"); if (i < _ngroups) return(sc.GetWeightsForPrevious()(i)); else return(sc.GetWeightsForCurrent()(i-_ngroups)); } EddyCatch double IndividualWeightsModel::GetDerivScale(unsigned int i) const EddyTry { if (i >= NDerivs()) throw EddyException("IndividualWeightsModel::GetDerivScale: i is out of range"); return(1e-5); } EddyCatch void IndividualWeightsModel::SetDerivParam(EDDY::ECScan& sc, unsigned int i, unsigned int s, // Not needed, but checked anyway double val) const EddyTry { if (i >= NDerivs()) throw EddyException("IndividualWeightsModel::SetDerivParam: i is out of range"); if (s >= _nscans) throw EddyException("IndividualWeightsModel::SetDerivParam: s is out of range"); if (i < _ngroups) { arma::colvec p = sc.GetWeightsForPrevious(); p(i) = val; sc.set_previous(p); } else { arma::colvec p = sc.GetWeightsForCurrent(); p(i-_ngroups) = val; sc.set_current(p); } return; } EddyCatch double IndividualWeightsModel::GetLevenbergLambda(unsigned int s) const EddyTry { if (s >= _nscans) throw EddyException("IndividualWeightsModel::GetLevenbergLambda: s is out of range"); return(_ll[s]); } EddyCatch void IndividualWeightsModel::SetLevenbergLambda(unsigned int s, double lambda) EddyTry { if (s >= _nscans) throw EddyException("IndividualWeightsModel::SetLevenbergLambda: s is out of range"); _ll[s] = lambda; return; } EddyCatch std::vector IndividualWeightsModel::GetParameters(const ECScanManager& sm) const EddyTry { // First verify that the parameters are consistent for (unsigned int s=0; s rval(_nscans); for (unsigned int s=0; s<_nscans; s++) rval[s] = arma::join_cols(_prev[s],_curr[s]); return(rval); } EddyCatch arma::colvec IndividualWeightsModel::GetParameters(const ECScan& sc, unsigned int s) const EddyTry { // First verify that the parameters are consistent if (s >= _nscans) throw EddyException("IndividualWeightsModel::GetParameters(sc,s): s out of bounds"); if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"IndividualWeightsModel::GetParameters(sc,s): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } return(arma::join_cols(_prev[s],_curr[s])); } EddyCatch void IndividualWeightsModel::UpdateParameters(ECScanManager& sm, const std::vector& update) EddyTry { // First check agreement between model and update if (update.size() != _nscans || update[0].n_rows != NDerivs()) { throw EddyException("IndividualWeightsModel::UpdateParameters(sm,update): size mismatch between update and model"); } // Next check agreement between model and scans if (sm.NScans() != _nscans) throw EddyException("IndividualWeightsModel::UpdateParameters(sm,update): size mismatch between sm.NScans() and _nscans"); for (unsigned int s=0; s= _nscans) throw EddyException("IndividualWeightsModel::UpdateParameters(sc,s,update): s out of bounds"); // Next check agreement between model and scans if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"IndividualWeightsModel::UpdateParameters(sc,s,update): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } // Now update parameters _prev[s] += update.head(_ngroups); _curr[s] += update.tail(_ngroups); sc.set_previous(_prev[s]); sc.set_current(_curr[s]); return; } EddyCatch nlohmann::json IndividualWeightsModel::WriteReport(const ECScanManager& sm, const std::string& fname, bool write_old_style_file) const EddyTry { if (_nscans != sm.NScans()) throw EddyException("IndividualWeightsModel::WriteReport: Mismatch between _nscans and sm.NScans()"); nlohmann::json long_ec_json; if (write_old_style_file) { try { std::ofstream fout; fout.open(fname.c_str(),std::ios::out|std::ios::trunc); fout << "Long time constant EC report for individual weights model. Two rows per volume." << std::endl; fout << "The first row gives the weights for the EC-field from the previous diffusion gradient." << std::endl; fout << "The second row gives the weights for the EC-field from the current diffusion gradient." << std::endl; fout << "Each column represents the weight for one MB-group" << std::endl; for (unsigned int s=0; s<_nscans; s++) { arma::colvec par = this->GetParameters(sm.Scan(s),s); arma::colvec previous = par.head_rows(_ngroups); for (unsigned int c=0; c<_ngroups; c++) fout << previous(c) << " "; fout << std::endl; arma::colvec current = par.tail_rows(_ngroups); for (unsigned int c=0; c<_ngroups; c++) fout << current(c) << " "; fout << std::endl; } fout.close(); } catch(...) { throw EddyException("IndividualWeightsModel::WriteReport: Error when attempting to write file " + fname); } } std::vector volumes(_nscans); for (unsigned int s=0; s<_nscans; s++) { volumes[s]["Volume #"] = s; arma::colvec par = this->GetParameters(sm.Scan(s),s); volumes[s]["Previous"] = arma::conv_to >::from(par.head_rows(_ngroups)); volumes[s]["Current"] = arma::conv_to >::from(par.tail_rows(_ngroups)); } long_ec_json["Model"] = "Individual weights"; long_ec_json["Format"] = "Long time-constant eddy currents was modelled as two sets of weights per volume,\nthe first is the weights of the EC field from the previous diffusion gradient\n and the second is the weights for the EC field for the current diffusion gradient.\nThe weights pertains to the MB-groups."; long_ec_json["Volumes"] = volumes; return(long_ec_json); } EddyCatch bool IndividualWeightsModel::model_and_scan_agree(const ECScan& sc, unsigned int s) const EddyTry { return(!(arma::any(sc.GetWeightsForPrevious() != _prev[s]) || arma::any(sc.GetWeightsForCurrent() != _curr[s]))); } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class IndividualTimeConstantsModel // // This class models the time constants (half-life in seconds) // on a volume-by-volume basis. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IndividualTimeConstantsModel::IndividualTimeConstantsModel(ECScanManager& sm) EddyTry : _nscans(sm.NScans()), _tai2w(sm.RepetitionTime(),sm.MultiBand()), _par(sm.NScans(),arma::colvec(4,arma::fill::none)), _ll(sm.NScans(),0.1) // Start with a fair bit of regularisation { if (!sm.IsDiffusion()) throw EddyException("IndividualTimeConstantsModel::IndividualTimeConstantsModel: sm is not diffusion"); for (unsigned int s=0; s<_nscans; s++) { _par[s](0) = TC_INITIAL_VALUE; _par[s](1) = INTCPT_INITIAL_VALUE; _par[s](2) = TC_INITIAL_VALUE; _par[s](3) = INTCPT_INITIAL_VALUE; } arma::colvec prev = _tai2w.GetWeightsForPrevious(_par[0](0),_par[0](1)); arma::colvec curr = _tai2w.GetWeightsForCurrent(_par[0](2),_par[0](3)); std::cout << "IndividualTimeConstantsModel::IndividualTimeConstantsModel" << std::endl; std::cout << "Previous:" << std::endl; for (unsigned int i=0; i= NDerivs()) throw EddyException("IndividualTimeConstantsModel::GetDerivParam: i out of bounds"); if (s >= _nscans) throw EddyException("IndividualTimeConstantsModel::GetDerivParam: s out of bounds"); return(_par[s](i)); } EddyCatch double IndividualTimeConstantsModel::GetDerivScale(unsigned int i) const EddyTry { if (i >= NDerivs()) throw EddyException("IndividualTimeConstantsModel::GetDerivScale: i out of bounds"); if (i==0 || i==2) return(1e-3); // If it is half life return(1e-4); // If it is intercept // return(1e-3); } EddyCatch void IndividualTimeConstantsModel::SetDerivParam(ECScan& sc, unsigned int i, unsigned int s, double val) const { if (i >= NDerivs()) throw EddyException("IndividualTimeConstantsModel::SetDerivParam: i out of bounds"); if (s >= _nscans) throw EddyException("IndividualTimeConstantsModel::SetDerivParam: s out of bounds"); if (i<2) { if (i==0) sc.set_previous(_tai2w.GetWeightsForPrevious(val,_par[s](1))); else sc.set_previous(_tai2w.GetWeightsForPrevious(_par[s](0),val)); } else { if (i==2) sc.set_current(_tai2w.GetWeightsForCurrent(val,_par[s](3))); else sc.set_current(_tai2w.GetWeightsForCurrent(_par[s](2),val)); } } double IndividualTimeConstantsModel::GetLevenbergLambda(unsigned int s) const EddyTry { if (s >= _nscans) throw EddyException("IndividualTimeConstantsModel::GetLevenbergLambda: s is out of range"); return(_ll[s]); } EddyCatch void IndividualTimeConstantsModel::SetLevenbergLambda(unsigned int s, double lambda) EddyTry { if (s >= _nscans) throw EddyException("IndividualTimeConstantsModel::SetLevenbergLambda: s is out of range"); _ll[s] = lambda; return; } EddyCatch std::vector IndividualTimeConstantsModel::GetParameters(const ECScanManager& sm) const EddyTry { // First verify that the parameters are consistent for (unsigned int s=0; s= _nscans) throw EddyException("IndividalTimeConstantsModel::GetParameters(sc,s): s out of bounds"); if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"IndividualTimeConstantsModel::GetParameters(sc,s): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } return(_par[s]); } EddyCatch void IndividualTimeConstantsModel::UpdateParameters(ECScanManager& sm, const std::vector& update) EddyTry { // First check agreement between model and update if (update.size() != _nscans || update[0].n_rows != NDerivs()) { throw EddyException("IndividualTimeConstantsModel::UpdateParameters(sm,update): size mismatch between update and model"); } // Next check agreement between model and scans if (sm.NScans() != _nscans) throw EddyException("IndividualTimeConstantsModel::UpdateParameters(sm,update): size mismatch between sm.NScans() and _nscans"); for (unsigned int s=0; s= _nscans) throw EddyException("IndividalTimeConstantsModel::UpdateParameters(sc,s,update): s out of bounds"); // Next check agreement between model and scans if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"IndividalTimeConstantsModel::UpdateParameters(sc,s,update): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } // Now update parameters for (unsigned int i=0; i tc = this->GetParameters(sm); for (unsigned int s=0; s<_nscans; s++) { fout << tc[s](0) << " " << tc[s](1) << " " << tc[s](2) << " " << tc[s](3) << std::endl; arma::colvec previous = sm.Scan(s).GetWeightsForPrevious(); for (unsigned int c=0; c<_tai2w.NGroups(); c++) fout << previous(c) << " "; fout << std::endl; arma::colvec current = sm.Scan(s).GetWeightsForCurrent(); for (unsigned int c=0; c<_tai2w.NGroups(); c++) fout << current(c) << " "; fout << std::endl; } fout.close(); } catch(...) { throw EddyException("IndividualTimeConstantsModel::WriteReport: Error when attempting to write file " + fname); } } nlohmann::json long_ec_json; std::vector volumes(_nscans); for (unsigned int s=0; s<_nscans; s++) { volumes[s]["Volume #"] = s; volumes[s]["Half-life (s) Intercept for previous"] = arma::conv_to >::from(this->GetParameters(sm.Scan(s),s).rows(0,1)); volumes[s]["Half-life (s) Intercept for current"] = arma::conv_to >::from(this->GetParameters(sm.Scan(s),s).rows(2,3)); volumes[s]["Resulting weights for previous"] = arma::conv_to >::from(sm.Scan(s).GetWeightsForPrevious()); volumes[s]["Resulting weights for current"] = arma::conv_to >::from(sm.Scan(s).GetWeightsForCurrent()); } long_ec_json["Model"] = "Individual time-constants"; long_ec_json["Format"] = "Long time-constant eddy currents was modelled as one time-constant (half-life in seconds) \nand one intercept for previous and current, resulting in four parameters per volume.\nThe resulting weights for the MB-groups are also given."; long_ec_json["Volumes"] = volumes; return(long_ec_json); } EddyCatch bool IndividualTimeConstantsModel::model_and_scan_agree(const ECScan& sc, unsigned int s) const EddyTry { return(!(arma::any(sc.GetWeightsForPrevious() != _tai2w.GetWeightsForPrevious(_par[s](0),_par[s](1))) || arma::any(sc.GetWeightsForCurrent() != _tai2w.GetWeightsForCurrent(_par[s](2),_par[s](3))))); } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class JointWeightsModel // // This class models the weights jointly across all volumes. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ JointWeightsModel::JointWeightsModel(ECScanManager& sm) EddyTry : _nscans(sm.NScans()), _ngroups(sm.MultiBand().NGroups()), _prev(arma::colvec(sm.MultiBand().NGroups(),arma::fill::zeros)), _curr(arma::colvec(sm.MultiBand().NGroups(),arma::fill::ones)), _ll(1e-6) // Start with very little regularisation { if (!sm.IsDiffusion()) throw EddyException("JointWeightsModel::JointWeightsModel: sm is not diffusion"); for (unsigned int s=0; s= _nscans) throw EddyException("JointWeightsModel::GetDerivParam: s is out of range"); if (i >= NDerivs()) throw EddyException("JointWeightsModel::GetDerivParam: i is out of range"); if (i < _ngroups) return(sc.GetWeightsForPrevious()(i)); else return(sc.GetWeightsForCurrent()(i-_ngroups)); } EddyCatch double JointWeightsModel::GetDerivScale(unsigned int i) const EddyTry { if (i >= NDerivs()) throw EddyException("JointWeightsModel::GetDerivScale: i is out of range"); return(1e-5); } EddyCatch void JointWeightsModel::SetDerivParam(EDDY::ECScan& sc, unsigned int i, unsigned int s, // Not needed, but checked anyway double val) const EddyTry { if (i >= NDerivs()) throw EddyException("JointWeightsModel::GetDerivParam: i is out of range"); if (s >= _nscans) throw EddyException("JointWeightsModel::GetDerivParam: s is out of range"); if (i < _ngroups) { arma::colvec p = sc.GetWeightsForPrevious(); p(i) = val; sc.set_previous(p); } else { arma::colvec p = sc.GetWeightsForCurrent(); p(i-_ngroups) = val; sc.set_current(p); } return; } EddyCatch double JointWeightsModel::GetLevenbergLambda(unsigned int s) const EddyTry { if (s >= _nscans) throw EddyException("JointWeightsModel::GetLevenbergLambda: s is out of range"); // Not needed, but do anyway return(_ll); } EddyCatch void JointWeightsModel::SetLevenbergLambda(unsigned int s, double lambda) EddyTry { if (s >= _nscans) throw EddyException("JointWeightsModel::SetLevenbergLambda: s is out of range"); // Not needed, but do anyway _ll = lambda; return; } EddyCatch std::vector JointWeightsModel::GetParameters(const ECScanManager& sm) const EddyTry { // First verify that the parameters are consistent for (unsigned int s=0; s rval(1); rval[0] = arma::join_cols(_prev,_curr); return(rval); } EddyCatch arma::colvec JointWeightsModel::GetParameters(const ECScan& sc, unsigned int s) const EddyTry { // First verify that the parameters are consistent if (s >= _nscans) throw EddyException("JointWeightsModel::GetParameters(sc,s): s out of bounds"); if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"JointWeightsModel::GetParameters(sc,s): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } return(arma::join_cols(_prev,_curr)); } EddyCatch void JointWeightsModel::UpdateParameters(ECScanManager& sm, const std::vector& update) EddyTry { // First check agreement between model and update if (update.size() != 1 || update[0].n_rows != NDerivs()) { throw EddyException("JointWeightsModel::UpdateParameters(sm,update): size mismatch between update and model"); } // Next check agreement between model and scans if (sm.NScans() != _nscans) throw EddyException("JointWeightsModel::UpdateParameters(sm,update): size mismatch between sm.NScans() and _nscans"); for (unsigned int s=0; s par = this->GetParameters(sm); arma::colvec previous = par[0].head_rows(_ngroups); for (unsigned int c=0; c<_ngroups; c++) fout << previous(c) << " "; fout << std::endl; arma::colvec current = par[0].tail_rows(_ngroups); for (unsigned int c=0; c<_ngroups; c++) fout << current(c) << " "; fout << std::endl; fout.close(); } catch(...) { throw EddyException("JointWeightsModel::WriteReport: Error when attempting to write file " + fname); } } nlohmann::json long_ec_json; long_ec_json["Model"] = "Joint weights"; long_ec_json["Format"] = "Long time-constant eddy currents was modelled as two sets of weights,\nthe first is the weights of the EC field from the previous diffusion gradient\n and the second is the weights for the EC field for the current diffusion gradient.\nThe weights pertains to the MB-groups and are pooled across all volumes."; std::vector par = this->GetParameters(sm); long_ec_json["Previous"] = arma::conv_to >::from(par[0].head_rows(_ngroups)); long_ec_json["Current"] = arma::conv_to >::from(par[0].tail_rows(_ngroups)); return(long_ec_json); } EddyCatch bool JointWeightsModel::model_and_scan_agree(const ECScan& sc, unsigned int s) const EddyTry { return(!(arma::any(sc.GetWeightsForPrevious() != _prev) || arma::any(sc.GetWeightsForCurrent() != _curr))); } EddyCatch //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // // Class JointTimeConstantModel // // This class models the time-constant (half-life in seconds) // jointly across all volumes. // //@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ JointTimeConstantModel::JointTimeConstantModel(ECScanManager& sm) EddyTry : _nscans(sm.NScans()), _tai2w(sm.RepetitionTime(),sm.MultiBand()), _par(arma::colvec(4,arma::fill::none)), _ll(0.1) // Start with a fair bit of regularisation { if (!sm.IsDiffusion()) throw EddyException("JointTimeConstantsModel::JointTimeConstantsModel: sm is not diffusion"); _par(0) = TC_INITIAL_VALUE; _par(1) = INTCPT_INITIAL_VALUE; _par(2) = TC_INITIAL_VALUE; _par(3) = INTCPT_INITIAL_VALUE; arma::colvec prev = _tai2w.GetWeightsForPrevious(_par(0),_par(1)); arma::colvec curr = _tai2w.GetWeightsForCurrent(_par(2),_par(3)); for (unsigned int s=0; s= NDerivs()) throw EddyException("JointTimeConstantModel::GetDerivParam: i out of bounds"); if (s >= _nscans) throw EddyException("JointTimeConstantModel::GetDerivParam: s out of bounds"); return(_par(i)); } EddyCatch double JointTimeConstantModel::GetDerivScale(unsigned int i) const EddyTry { if (i >= NDerivs()) throw EddyException("JointTimeConstantModel::GetDerivScale: i out of bounds"); return(1e-5); } EddyCatch void JointTimeConstantModel::SetDerivParam(ECScan& sc, unsigned int i, unsigned int s, double val) const { if (i >= NDerivs()) throw EddyException("JointTimeConstantModel::SetDerivParam: i out of bounds"); if (s >= _nscans) throw EddyException("JointTimeConstantModel::SetDerivParam: s out of bounds"); if (i < 2) { if (i==0) sc.set_previous(_tai2w.GetWeightsForPrevious(val,_par(1))); else sc.set_previous(_tai2w.GetWeightsForPrevious(_par(0),val)); } else { if (i==2) sc.set_current(_tai2w.GetWeightsForCurrent(val,_par(3))); else sc.set_current(_tai2w.GetWeightsForCurrent(_par(2),val)); } } double JointTimeConstantModel::GetLevenbergLambda(unsigned int s) const EddyTry { if (s >= _nscans) throw EddyException("JointTimeConstantModel::GetLevenbergLambda: s is out of range"); // Not needed, but do anyway return(_ll); } EddyCatch void JointTimeConstantModel::SetLevenbergLambda(unsigned int s, double lambda) EddyTry { if (s >= _nscans) throw EddyException("JointTimeConstantModel::SetLevenbergLambda: s is out of range"); // Not needed, but do anyway _ll = lambda; return; } EddyCatch std::vector JointTimeConstantModel::GetParameters(const ECScanManager& sm) const EddyTry { // First verify that the parameters are consistent for (unsigned int s=0; s(1,_par)); } EddyCatch arma::colvec JointTimeConstantModel::GetParameters(const ECScan& sc, unsigned int s) const EddyTry { // First verify that the parameters are consistent if (s >= _nscans) throw EddyException("JointTimeConstantModel::GetParameters(sc,s): s out of bounds"); if (!model_and_scan_agree(sc,s)) { char message[256]; sprintf(message,"JointTimeConstantModel::GetParameters(sc,s): mismatch between model and scan %u",s); throw EddyException(std::string(message)); } return(_par); } EddyCatch void JointTimeConstantModel::UpdateParameters(ECScanManager& sm, const std::vector& update) EddyTry { // First check agreement between model and update if (update.size() != 1 || update[0].n_rows != NDerivs()) { throw EddyException("JointTimeConstantModel::UpdateParameters(sm,update): size mismatch between update and model"); } // Next check agreement between model and scans if (sm.NScans() != _nscans) throw EddyException("JointTimeConstantModel::UpdateParameters(sm,update): size mismatch between sm.NScans() and _nscans"); for (unsigned int s=0; s tc = this->GetParameters(sm); fout << tc[0](0) << " " << tc[0](1) << " " << tc[0](2) << " " << tc[0](3) << std::endl; arma::colvec previous = sm.Scan(0).GetWeightsForPrevious(); for (unsigned int c=0; c<_tai2w.NGroups(); c++) fout << previous(c) << " "; fout << std::endl; arma::colvec current = sm.Scan(0).GetWeightsForCurrent(); for (unsigned int c=0; c<_tai2w.NGroups(); c++) fout << current(c) << " "; fout << std::endl; fout.close(); } catch(...) { throw EddyException("JointTimeConstantModel::WriteReport: Error when attempting to write file " + fname); } } nlohmann::json long_ec_json; long_ec_json["Model"] = "Joint time-constant"; long_ec_json["Format"] = "Long time-constant eddy currents was modelled as a single time-constant (half-life in seconds) across all volumes.\nThe resulting weights for the MB-groups are also given."; long_ec_json["Half-life (s) Intercept for previous"] = arma::conv_to >::from(this->GetParameters(sm)[0].rows(0,1)); long_ec_json["Half-life (s) Intercept for current"] = arma::conv_to >::from(this->GetParameters(sm)[0].rows(2,3)); long_ec_json["Resulting weights for previous"] = arma::conv_to >::from(sm.Scan(0).GetWeightsForPrevious()); long_ec_json["Resulting weights for current"] = arma::conv_to >::from(sm.Scan(0).GetWeightsForCurrent()); return(long_ec_json); } EddyCatch bool JointTimeConstantModel::model_and_scan_agree(const ECScan& sc, unsigned int s) const EddyTry { return(!(arma::any(sc.GetWeightsForPrevious() != _tai2w.GetWeightsForPrevious(_par(0),_par(1))) || arma::any(sc.GetWeightsForCurrent() != _tai2w.GetWeightsForCurrent(_par(2),_par(3))))); } EddyCatch nlohmann::json NoLongECModel::WriteReport(const ECScanManager& sm, const std::string& fname, bool write_old_style_file) const EddyTry { nlohmann::json long_ec_json; long_ec_json["Model"] = "None"; long_ec_json["Format"] = "No modelling of long time-constant eddy currents was performed."; return(long_ec_json); } EddyCatch