Skip to content

Commit

Permalink
1. bug fix: in onPushRun.m the adiabatic quality p was incorrectly …
Browse files Browse the repository at this point in the history
…calculated when the total simulation time exceeded the actual switch-off ramp time

2. bug fix: both axes for the pulse frequency and current modulation are now show correctly when exporting the *Current View* figure from the menu
3. new feature: an animation of the latest result can be played for all parameter combinations (e.g. *Pulse + Relaxation* or *Pre-polarization switch-off + Pulse + Relaxation*, etc.)
4. typo fixing within comments and `README.md`
  • Loading branch information
ThoHiller committed May 16, 2020
1 parent 44a9887 commit e74ecb4
Show file tree
Hide file tree
Showing 45 changed files with 3,462 additions and 2,061 deletions.
4 changes: 2 additions & 2 deletions BLOCHUS/BLOCHUS.m
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
%------------- BEGIN CODE --------------

%% GUI 'header' info and default GUI settings
myui.version = '0.1.0';
myui.date = '15.05.2020';
myui.version = '0.1.1';
myui.date = '16.05.2020';
myui.author = 'Thomas Hiller';
myui.email = 'thomas.hiller[at]leibniz-liag.de';

Expand Down
2 changes: 1 addition & 1 deletion BLOCHUS/BLOCHUS_createPanelBasic.m
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@
'FontSize',myui.fontsize,...
'UserData',struct('Tooltipstr',tstr,'defaults',data.init.B0),...
'Callback',@onEditValue);
tstr = '<HTML><b>Omega</b><sub>0</sub> frequency in [Hz].<br>';
tstr = ['<HTML>Larmor frequency <b>',char(hex2dec('3C9')),'</b><sub>0</sub> in [Hz].<br>'];
gui.edit_handles.Omega0 = uicontrol('Style','Edit',...
'Parent',setB0,...
'String',sprintf('%7.2f',data.init.Omega0(1)),...
Expand Down
2 changes: 1 addition & 1 deletion BLOCHUS/BLOCHUS_createPanelControl.m
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
'UserData',struct('Tooltipstr',tstr),...
'Callback',@onPushRun);

tstr = 'Show animation of the last result.';
tstr = 'Show animation of the latest result.';
gui.push_handles.Animate = uicontrol('Style','pushbutton',...
'Parent',gui.panels.Control.HBox,...
'String','ANIMATE',...
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#### Changelog

##### Version v.0.1.1

1. bug fix: in `onPushRun.m` the adiabatic quality p was incorrectly calculated when the total simulation time exceeded the actual switch-off ramp time
2. bug fix: both axes for the pulse frequency and current modulation are now show correctly when exporting the *Current View* figure from the menu
3. new feature: an animation of the latest result can be played for all parameter combinations (e.g. *Pulse + Relaxation* or *Pre-polarization switch-off + Pulse + Relaxation*, etc.)
4. typo fixing within comments and `README.md`


##### Version v.0.1.0

Initial Version
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
**BLOCHUS** is a set of MATLAB<sup>TM</sup> tools, that allow some basic simulations of (S)NMR spin dynamics based on the Bloch equations. The Bloch equations are solved in the laboratory frame of reference with MATLABs built-in `ode45` solver. Because it was developed within the scope of a near surface SNMR project, its main features are the simulation of (1) pre-polarization switch-off ramps and (2) excitation pulses. The main front-end to the underlying simulation tools is a graphical user interface (GUI) that allows playing around with the different features and helps to understand the basic concepts of (S)NMR spin dynamics.

#### Basic features:
1. Choose between different protons (e.g. *Hydrogen*, *Helium*, *Flourine*, etc.)
1. Choose between different protons (e.g. *Hydrogen*, *Helium*, *Fluorine*, etc.)
2. Choose between different pre-polarization switch-off ramp shapes (e.g. *exponential*, *linear*, *half cosine*, etc.) with arbitrary ramp time
3. Choose between different excitation pulses (*pi/2*, *pi*, *adiabatic half passage*, etc.) with arbitrary off-resonant frequency, or in case of the adiabatic pulses, arbitrary frequency and current modulation.
3. Choose between different excitation pulses (*pi/2*, *pi*, *adiabatic half passage*, etc.) with arbitrary off-resonance frequency, or in case of the adiabatic pulses, arbitrary frequency and current modulation.

- - -

Expand Down
2 changes: 2 additions & 0 deletions callbacks/checkbox/onCheckPrePolPulse.m
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ function onCheckPrePolPulse(src,~)
data = getappdata(fig,'data');
end

% because the settings changed, deactivate the "Animate" button
set(gui.push_handles.Animate,'Enable','off');
% update all data inside the GUI
setappdata(fig,'data',data);
% update status bar
Expand Down
2 changes: 1 addition & 1 deletion callbacks/checkbox/onCheckPulseQ.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function onCheckPulseQ(src,~)
% get GUI data
data = getappdata(fig,'data');
% plot pulse
plotBpulse(gui.figh);
plotPulse(gui.figh);

else
warndlg({'onCheckPulseQ:','There is no figure with the BLOCHUS Tag open.'},...
Expand Down
82 changes: 37 additions & 45 deletions callbacks/edits/onEditValue.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function onEditValue(src,~)
% getOmega0
% getPulseParameters
% getRampParameters
% plotBpulse
% plotPulse
% plotRamp
%
% Subfunctions:
Expand Down Expand Up @@ -50,37 +50,20 @@ function onEditValue(src,~)
% get the user data of the field
ud = get(src,'UserData');

if isstruct(ud) % for new fields
% get the default values [default min max]
defaults = ud.defaults;

% check if the value is numeric
% if not reset to defaults stored in user data
if isnan(val)
set(src,'String',num2str(defaults(1)));
val = str2double(get(src,'String'));
end
% check if the value is out of bounds
% if yes reset to default
if val < defaults(2) || val > defaults(3)
set(src,'String',num2str(defaults(1)));
val = str2double(get(src,'String')); %#ok<*NASGU>
end

else % old style (Needs to be removed after the refactoring)
% check if the value is numeric
% if not reset to defaults stored in user data
if isnan(val)
set(src,'String',num2str(ud(3)));
val = str2double(get(src,'String'));
end
% check if the value is out of bounds
% if yes reset to default
if val < ud(1) || val > ud(2)
set(src,'String',num2str(ud(3)));
val = str2double(get(src,'String')); %#ok<*NASGU>
end
disp(['there is still an old style edit field:',tag])
% get the default values [default min max]
defaults = ud.defaults;

% check if the value is numeric
% if not reset to defaults stored in user data
if isnan(val)
set(src,'String',num2str(defaults(1)));
val = str2double(get(src,'String'));
end
% check if the value is out of bounds
% if yes reset to default
if val < defaults(2) || val > defaults(3)
set(src,'String',num2str(defaults(1)));
val = str2double(get(src,'String')); %#ok<*NASGU>
end

% get the data field to update from the field tag
Expand Down Expand Up @@ -175,6 +158,7 @@ function onEditValue(src,~)
% -------------------------------------------------------------
% --- PRE-POLARIZATION ----------------------------------------
% -------------------------------------------------------------

case {'prepol_Factor','prepol_SwitchFactor'}
% check if the ramp type is "melton1995", if yes update the
% switch-off time to maintain the same switch-off rate
Expand Down Expand Up @@ -231,6 +215,7 @@ function onEditValue(src,~)
% -------------------------------------------------------------
% --- PULSE ---------------------------------------------------
% -------------------------------------------------------------

case 'pulse_B1Factor'
% if the pulse amplitude changes some settings have to be
% adjusted, depending on the chosen pulse type
Expand All @@ -239,10 +224,12 @@ function onEditValue(src,~)
switch data.pulse.Type
case 'pi_half'
% in case of "pi_half" the pulse length gets updated
data.pulse.Ttau = abs(1e3*(pi/2/(data.basic.gamma*data.basic.B0*data.pulse.B1Factor)));
data.pulse.Ttau = abs(1e3*(pi/2/(data.basic.gamma*...
data.basic.B0*data.pulse.B1Factor)));
case 'pi'
% in case of "pi" the pulse length gets updated
data.pulse.Ttau = abs(1e3*(pi/(data.basic.gamma*data.basic.B0*data.pulse.B1Factor)));
data.pulse.Ttau = abs(1e3*(pi/(data.basic.gamma*...
data.basic.B0*data.pulse.B1Factor)));
end
set(gui.edit_handles.PulseTtau,'String',num2str(data.pulse.Ttau));
% in case of pure "Pulse" update the total simulation time
Expand All @@ -257,13 +244,13 @@ function onEditValue(src,~)
% update pulse settings
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);

otherwise
% update pulse settings
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);
end

case 'pulse_Ttau'
Expand All @@ -274,10 +261,12 @@ function onEditValue(src,~)
switch data.pulse.Type
case 'pi_half'
% in case of "pi_half" the pulse amplitude gets updated
data.pulse.B1Factor = abs(1e3*(pi/2/(data.basic.gamma*data.basic.B0*data.pulse.Ttau)));
data.pulse.B1Factor = abs(1e3*(pi/2/(data.basic.gamma*...
data.basic.B0*data.pulse.Ttau)));
case 'pi'
% in case of "pi" the pulse amplitude gets updated
data.pulse.B1Factor = abs(1e3*(pi/(data.basic.gamma*data.basic.B0*data.pulse.Ttau)));
data.pulse.B1Factor = abs(1e3*(pi/(data.basic.gamma*...
data.basic.B0*data.pulse.Ttau)));
end
set(gui.edit_handles.PulseB1Factor,'String',num2str(data.pulse.B1Factor));
% in case of pure "Pulse" update the total simulation time
Expand All @@ -292,7 +281,7 @@ function onEditValue(src,~)
% update pulse settings
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);

case 'MIDI_OR'
% in case of "MIDI_OR" the PulseTtau-field holds the
Expand All @@ -301,7 +290,8 @@ function onEditValue(src,~)
% update pulse length
data.pulse.Ttau = abs(data.pulse.MIDINP/data.basic.Omega0)*1e3;
% update pulse amplitude to pi/2 value
data.pulse.B1Factor = abs(1e3*(pi/2/(data.basic.gamma*data.basic.B0*data.pulse.Ttau)));
data.pulse.B1Factor = abs(1e3*(pi/2/(data.basic.gamma*...
data.basic.B0*data.pulse.Ttau)));
set(gui.edit_handles.PulseB1Factor,'String',num2str(data.pulse.B1Factor));
% in case of pure "Pulse" update the total simulation time
switch data.basic.type
Expand All @@ -315,7 +305,7 @@ function onEditValue(src,~)
% update pulse settings
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);

case 'MIDI_AP'
% in case of "MIDI_AP" the PulseTtau-field holds the
Expand All @@ -339,7 +329,7 @@ function onEditValue(src,~)
end
setappdata(fig,'data',data);
% update pulse plot
plotBpulse(fig);
plotPulse(fig);

otherwise
% for all other pulse types ("free" & "AHP")
Expand All @@ -356,7 +346,7 @@ function onEditValue(src,~)
% update pulse settings
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);
end

otherwise
Expand All @@ -366,10 +356,12 @@ function onEditValue(src,~)
setappdata(fig,'data',data);
getPulseParameters(fig);
data = getappdata(fig,'data');
plotBpulse(fig);
plotPulse(fig);

otherwise
% nothing to do for all other fields e.g. T1, T2, Tsim
% because the pulse data changed, deactivate the
% "Animate" button
set(gui.push_handles.Animate,'Enable','off');
end
end

Expand Down
92 changes: 77 additions & 15 deletions callbacks/menus/onMenuViewFigure.m
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,70 @@ function onMenuViewFigure(src,~)
% opening the export figure
expfig = figure;

% create the axes layout on the export figure
% due to the two axes on the pulse modulation panel, we need to know
% before creating the new figure if this panel is active
isdual = false;
if get(gui.panels.Plot.Pulse,'Selection') == 1
isdual = true;
end

% create the axes layout on the export figure and get the axes
% positions
switch get(src,'Label')
case {'Current View','Magnetization','Switch-off Ramp'}
case 'Current View'
% we copy all visible axes in a 2x2 grid
ax1 = subplot(2,2,1,'Parent',expfig);
ax2 = subplot(2,2,2,'Parent',expfig);
ax3 = subplot(2,2,3,'Parent',expfig);
pos1 = get(ax1,'Position');
pos2 = get(ax2,'Position');
pos3 = get(ax3,'Position');
delete(ax1);
delete(ax2);
delete(ax3);
if isdual
ax4a = subplot(2,4,7,'Parent',expfig);
ax4b = subplot(2,4,8,'Parent',expfig);
pos4a = get(ax4a,'Position');
pos4b = get(ax4b,'Position');
delete(ax4a);
delete(ax4b);
else
ax4 = subplot(2,2,4,'Parent',expfig);
pos4 = get(ax4,'Position');
delete(ax4);
end

case {'Magnetization','Switch-off Ramp'}
% we copy all visible axes in a 2x2 grid
ax1 = subplot(2,2,1,'Parent',expfig);
ax2 = subplot(2,2,2,'Parent',expfig);
ax3 = subplot(2,2,3,'Parent',expfig);
ax4 = subplot(2,2,4,'Parent',expfig);
pos1 = get(ax1,'Position');
pos2 = get(ax2,'Position');
pos3 = get(ax3,'Position');
pos4 = get(ax4,'Position');
delete(ax1);
delete(ax2);
delete(ax3);
delete(ax4);

case 'Pulse'
% we copy pulse parameter in a 3x2 grid
ax1 = subplot(3,2,1,'Parent',expfig);
ax2 = subplot(3,2,2,'Parent',expfig);
ax3 = subplot(3,2,[3 4],'Parent',expfig);
ax4 = subplot(3,2,[5 6],'Parent',expfig);
pos1 = get(ax1,'Position');
pos2 = get(ax2,'Position');
pos3 = get(ax3,'Position');
pos4 = get(ax4,'Position');
delete(ax1);
delete(ax2);
delete(ax3);
delete(ax4);
end
% get the default positions
pos1 = get(ax1,'Position');
pos2 = get(ax2,'Position');
pos3 = get(ax3,'Position');
pos4 = get(ax4,'Position');
% delete axes
delete(ax1);delete(ax2);delete(ax3);delete(ax4);

% copy the GUI axes to the export figure
switch get(src,'Label')
Expand Down Expand Up @@ -99,7 +140,8 @@ function onMenuViewFigure(src,~)
end
switch pul
case 1
ax4 = copyobj(gui.axes_handles.PulseSetupF,expfig);
ax4a = copyobj(gui.axes_handles.PulseSetupF,expfig);
ax4b = copyobj(gui.axes_handles.PulseSetupI,expfig);
case 2
ax4 = copyobj(gui.axes_handles.PulseB,expfig);
case 3
Expand Down Expand Up @@ -132,11 +174,26 @@ function onMenuViewFigure(src,~)

set(expfig,'Name','BLOCHUS: Pulse');
end

% adjust the axes positions
set(ax1,'Position',pos1);
set(ax2,'Position',pos2);
set(ax3,'Position',pos3);
set(ax4,'Position',pos4);
switch get(src,'Label')
case 'Current View'
set(ax1,'Position',pos1);
set(ax2,'Position',pos2);
set(ax3,'Position',pos3);
if isdual
set(ax4a,'Position',pos4a);
set(ax4b,'Position',pos4b);
else
set(ax4,'Position',pos4);
end

otherwise
set(ax1,'Position',pos1);
set(ax2,'Position',pos2);
set(ax3,'Position',pos3);
set(ax4,'Position',pos4);
end

% adjust the position of the export figure
set(expfig,'Position',[posf(1)+300 posf(2) (posf(3)-300)*0.8 posf(4)*0.8]);
Expand All @@ -146,7 +203,12 @@ function onMenuViewFigure(src,~)
case 'Current View'
lgh1 = legend(ax1,'show');
lgh3 = legend(ax3,'show');
lgh4 = legend(ax4,'show');
if isdual
lgh4a = legend(ax4a,'show');
lgh4b = legend(ax4b,'show');
else
lgh4 = legend(ax4,'show');
end

case 'Magnetization'
lgh1 = legend(ax1,'show');
Expand Down
Loading

0 comments on commit e74ecb4

Please sign in to comment.