Skip to content

Commit

Permalink
add plot sequence (#23)
Browse files Browse the repository at this point in the history
* update readme

* version update

* no default config generation

* add some checks to config reader

* add log

* update tests

* .

* update

* add test

* change format of vectors in config

* version update

* plot sequence
  • Loading branch information
aghaeifar authored Jan 5, 2025
1 parent 4234315 commit 3174590
Show file tree
Hide file tree
Showing 16 changed files with 374 additions and 278 deletions.
34 changes: 10 additions & 24 deletions config/config_default.ini
Original file line number Diff line number Diff line change
Expand Up @@ -34,38 +34,24 @@ T2[1] = 41
; repetition time in microsecond (integer)
TR = 10e3
; echo time in microsecond (integer)
TE[0] = 5e3
TE[1] = 6e3
TE[2] = 7e3
TE = 5e3 6e3 7e3
; RF Flip angle in degree (float)
RF_FA[0] = 15.0
RF_FA[1] = 0.0
RF_FA[2] = 0.0
RF_FA = 15.0 0.0 0.0
; RF Phase in degree (float). Note PHASE_CYCLING will be added to the phase of the first RF
RF_PH[0] = 0.0
RF_PH[1] = 0.0
RF_PH[2] = 0.0
RF_PH = 0.0 0.0 0.0
; Time to apply RF in microsecond (integer). The first RF start time is always 0.0
RF_T[0] = 0
RF_T[1] = 100e3
RF_T[2] = 200e3
RF_T = 0 100e3 200e3
; Dephasing in degree (float). The initial spin in the population will experience a dephasing of 0.0 degrees. Dephasing will then progressively increase in a linear manner up to the final spin, which will undergo dephasing as specified by the given parameter
DEPHASING[0] =
DEPHASING[1] =
DEPHASING[2] =
DEPHASING =
; Time to apply dephasing in microsecond (integer).
DEPHASING_T[0] =
DEPHASING_T[1] =
DEPHASING_T[2] =
DEPHASING_T =
; Gradient in mT/m for each axis (float). Each sample is active for one TIME_STEP
; GRADIENT_XYZ[x] = 1.0 2.2 1.5
GRADIENT_XYZ[0] =
GRADIENT_XYZ[1] =
GRADIENT_XYZ[2] =
GRADIENT_X =
GRADIENT_Y =
GRADIENT_Z =
; Time to apply gradient in micro-second (integer).
GRADIENT_T[0] =
GRADIENT_T[1] =
GRADIENT_T[2] =
GRADIENT_T =
; time intervals per random-walk in micro-second (integer)
TIME_STEP = 50
; number of dummy scans to reach steady state. The first RF pulse (RF_FA[0]) is used for excitation in dummy scans. If negative, it will be set to 5T1/TR.
Expand Down
31 changes: 9 additions & 22 deletions config/gradient.ini
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,21 @@ T2[0] = 1000e3

[SCAN_PARAMETERS]
TR = 40e3
TE[0] = 20e3
TE = 20e3

RF_FA[0] = 90.0
RF_FA = 90.0
RF_PH = 90.0
RF_T = 0

RF_PH[0] = 90.0
GRADIENT_X = 521.9377
GRADIENT_Y = 0.0
GRADIENT_Z = 0.0
GRADIENT_T = 10e3

RF_T[0] = 0

DEPHASING[0] =
DEPHASING[1] =
DEPHASING[2] =

DEPHASING_T[0] =
DEPHASING_T[1] =
DEPHASING_T[2] =

;
GRADIENT_XYZ[0] = 521.9377 0.0 0.
GRADIENT_XYZ[1] =
GRADIENT_XYZ[2] =

GRADIENT_T[0] = 10e3
GRADIENT_T[1] =
GRADIENT_T[2] =

TIME_STEP = 50

[SIMULATION_PARAMETERS]
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation
NUMBER_OF_SPINS = 1030301
FOV_SCALE[0] = 1.0
SCALE[0] = 1.0
11 changes: 4 additions & 7 deletions config/gre.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ SEQ_NAME = gre

[SCAN_PARAMETERS]
TR = 40000
TE[0] = 20000

RF_FA[0] = 90.0

RF_PH[0] = 0.0

RF_T[0] = 0
TE = 20000
RF_FA = 90.0
RF_PH = 0.0
RF_T = 0

[SIMULATION_PARAMETERS]
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation
Expand Down
13 changes: 4 additions & 9 deletions config/se.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,11 @@ SEQ_NAME = se

[SCAN_PARAMETERS]
TR = 40000
TE[0] = 20000
TE = 20000

RF_FA[0] = 90.0
RF_FA[1] = 180.0

RF_PH[0] = 0.0
RF_PH[1] = 90.0

RF_T[0] = 0
RF_T[1] = 10000
RF_FA = 90.0 180.0
RF_PH = 0.0 90
RF_T = 0 10000

[SIMULATION_PARAMETERS]
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation
Expand Down
11 changes: 4 additions & 7 deletions config/ssfp.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ SEQ_NAME = ssfp

[SCAN_PARAMETERS]
TR = 10000
TE[0] = 5000

RF_FA[0] = 16.0

RF_PH[0] = 0.0

RF_T[0] = 0
TE = 5000
RF_FA = 16.0
RF_PH = 0.0
RF_T = 0

DUMMY_SCAN = -1
PHASE_CYCLING = 180
Expand Down
32 changes: 8 additions & 24 deletions config/stimulated_echo.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,14 @@ SEQ_NAME = ste

[SCAN_PARAMETERS]
TR = 120e3
TE[0] = 40e3
TE[1] = 80e3

RF_FA[0] = 90.0
RF_FA[1] = 90.0
RF_FA[2] = 90.0

RF_PH[0] = 0.0
RF_PH[1] = 90.0
RF_PH[2] = 90.0

RF_T[0] = 0
RF_T[1] = 20e3
RF_T[2] = 60e3

DEPHASING[0] = 360
DEPHASING[1] = 360
DEPHASING[2] = 720
DEPHASING[3] = 360

DEPHASING_T[0] = 10e3
DEPHASING_T[1] = 30e3
DEPHASING_T[2] = 50e3
DEPHASING_T[3] = 70e3
TE = 40e3 80e3

RF_FA = 90.0 90.0 90.0
RF_PH = 0.0 90.0 90.0
RF_T = 0 20e3 60e3

DEPHASING = 360 360 720 360
DEPHASING_T = 10e3 30e3 50e3 70e3

[SIMULATION_PARAMETERS]
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation
Expand Down
92 changes: 92 additions & 0 deletions others/plot_seq.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#!/usr/bin/env python

import os
import sys
import configparser
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle

def read_ini(file_path):
config = configparser.ConfigParser()
config.optionxform = str # Preserve case of keys
try:
config_parent_dict = {}
config.read(file_path)
if config.has_section('GENERAL'):
if config.has_option('GENERAL', 'PARENT_CONFIG') and config.get('GENERAL', 'PARENT_CONFIG') != '':
parent_config = config.get('GENERAL', 'PARENT_CONFIG')
parent_config_path = os.path.join(os.path.dirname(file_path), parent_config)
config_parent_dict = read_ini(parent_config_path)

config_dict = {section: dict(config.items(section)) for section in config.sections()}
config_combined_dict = {}
all_keys = config_parent_dict.keys() | config_dict.keys()
for key in all_keys:
if key not in config_parent_dict:
config_parent_dict[key] = {}
if key not in config_dict:
config_dict[key] = {}
config_combined_dict[key] = {**config_parent_dict[key], **config_dict[key]}

except Exception as e:
print(f"Error reading INI file: {e}")
sys.exit(1)

return config_combined_dict

def main():
if len(sys.argv) != 2:
print("Usage: python plot_seq.py <file_path>")
sys.exit(1)
file_path = sys.argv[1]

if os.path.exists(file_path) is False :
print(f"The file '{file_path}' does not exist.")
sys.exit(1)

config = read_ini(file_path)

TR = np.float64(config['SCAN_PARAMETERS']['TR'])
TE = np.fromstring(config['SCAN_PARAMETERS']["TE"], sep=' ')

RF_T = np.fromstring(config['SCAN_PARAMETERS']["RF_T"], sep=' ')
RF_FA = np.fromstring(config['SCAN_PARAMETERS']["RF_FA"], sep=' ')
RF_PH = np.fromstring(config['SCAN_PARAMETERS']["RF_PH"], sep=' ')

GRADIENT_T = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_T"], sep=' ')
GRADIENT_X = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_X"], sep=' ')
GRADIENT_Y = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_Y"], sep=' ')
GRADIENT_Z = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_Z"], sep=' ')

fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 6))
for t, h in zip(RF_T, RF_FA):
ax1.annotate('', xy=(t, h), xytext=(t, 0), arrowprops=dict(arrowstyle="->"))

TE_h = np.max(RF_FA)*0.75
for te in TE:
ax1.add_patch(Rectangle((te - 50, 0), 100, TE_h, color='red'))

ax1.set_xlim(-TR/10, TR)
ax1.set_ylim(0, np.max(RF_FA)*1.1)
ax1.spines['top'].set_visible(False)
ax1.spines['right'].set_visible(False)
ax1.set_ylabel('Flip Angle (deg)')

ax2.plot(GRADIENT_T, GRADIENT_X, label='Gradient X')
ax2.plot(GRADIENT_T, GRADIENT_Y, label='Gradient Y')
ax2.plot(GRADIENT_T, GRADIENT_Z, label='Gradient Z')
max_grad = np.max([np.max(GRADIENT_X), np.max(GRADIENT_Y), np.max(GRADIENT_Z)])
min_grad = np.min([np.min(GRADIENT_X), np.min(GRADIENT_Y), np.min(GRADIENT_Z)])
ax2.set_xlim(-TR/10, TR)
ax2.set_ylim(min_grad*1.1, max_grad*1.1)
ax2.spines['top'].set_visible(False)
ax2.spines['right'].set_visible(False)
ax2.legend()
ax2.set_xlabel('Time (ms)')
ax2.set_ylabel('Gradient Amplitude (mT/m)')

plt.show()

if __name__ == "__main__":
main()
49 changes: 26 additions & 23 deletions src/config/config_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,25 @@ bool config_generator::generate_default_config(uint32_t TE_us, uint32_t timestep
// repetition time in microsecond (integer)
ini_parent["SCAN_PARAMETERS"]["TR"] = std::to_string(TE_us + timestep_us);
// echo time in microsecond (integer)
ini_parent["SCAN_PARAMETERS"]["TE[0]"] = std::to_string(TE_us);
ini_parent["SCAN_PARAMETERS"]["TE"] = std::to_string(TE_us);
// RF Flip angle in degree (float)
ini_parent["SCAN_PARAMETERS"]["RF_FA[0]"] = "90.0";
ini_parent["SCAN_PARAMETERS"]["RF_FA"] = "90.0";
// RF Phase in degree (float). Note PHASE_CYCLING will be added to the phase of the first RF
ini_parent["SCAN_PARAMETERS"]["RF_PH[0]"] = "0.0";
ini_parent["SCAN_PARAMETERS"]["RF_PH"] = "0.0";
// Time to apply RF in microsecond (integer). The first RF start time is always 0.0
ini_parent["SCAN_PARAMETERS"]["RF_T[0]"] = "0";
ini_parent["SCAN_PARAMETERS"]["RF_T"] = "0";

// Dephasing in degree (float). The initial spin in the population will experience a dephasing of 0.0 degrees. Dephasing will then progressively increase in a linear manner up to the final spin, which will undergo dephasing as specified by the given parameter
ini_parent["SCAN_PARAMETERS"]["DEPHASING[0]"] = "";
ini_parent["SCAN_PARAMETERS"]["DEPHASING"] = "";
// Time to apply dephasing in microsecond (integer).
ini_parent["SCAN_PARAMETERS"]["DEPHASING_T[0]"] = "";
ini_parent["SCAN_PARAMETERS"]["DEPHASING_T"] = "";
// Gradient in mT/m for each axis (float). Each sample is active for one TIME_STEP
ini_parent["SCAN_PARAMETERS"]["GRADIENT_XYZ[0]"] = "";
ini_parent["SCAN_PARAMETERS"]["GRADIENT_X"] = "";
ini_parent["SCAN_PARAMETERS"]["GRADIENT_Y"] = "";
ini_parent["SCAN_PARAMETERS"]["GRADIENT_Z"] = "";
// Time to apply gradient in micro-second (integer).
ini_parent["SCAN_PARAMETERS"]["GRADIENT_T[0]"] = "";
ini_parent["SCAN_PARAMETERS"]["GRADIENT_T"] = "";

// time intervals per random-walk in micro-second (integer)
ini_parent["SCAN_PARAMETERS"]["TIME_STEP"] = std::to_string(timestep_us);
// number of dummy scans to reach steady state. The first RF pulse (RF_FA[0]) is used for excitation in dummy scans. If negative, it will be set to 5T1/TR.
Expand Down Expand Up @@ -98,10 +101,11 @@ bool config_generator::generate_gre(uint32_t TE_us, uint32_t timestep_us, std::v
add_param("FILES", "PHANTOM[" + std::to_string(i) + "]", phantoms[i]);

add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us + timestep_us));
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA[0]", "90.0");
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA", "90.0");
add_param("SCAN_PARAMETERS", "RF_PH", "0");
add_param("SCAN_PARAMETERS", "RF_T", "0");

add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));

if(output.empty() == false)
Expand All @@ -124,19 +128,17 @@ bool config_generator::generate_se(uint32_t TE_us, uint32_t timestep_us, std::ve
add_param("FILES", "PHANTOM[" + std::to_string(i) + "]", phantoms[i]);

add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us + timestep_us));
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA[0]", "90.0");
add_param("SCAN_PARAMETERS", "RF_FA[1]", "180.0");
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
add_param("SCAN_PARAMETERS", "RF_PH[1]", "90");
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
add_param("SCAN_PARAMETERS", "RF_T[1]", std::to_string(TE_us/2));
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA", "90.0 180.0");
add_param("SCAN_PARAMETERS", "RF_PH", "0 90");
add_param("SCAN_PARAMETERS", "RF_T", "0 " + std::to_string(TE_us/2));
add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));

if(output.empty() == false)
if(write_ini(output) == false)
return false;


return true;
}

Expand All @@ -153,10 +155,11 @@ bool config_generator::generate_bssfp(uint32_t TE_us, uint32_t timestep_us, std:
add_param("FILES", "PHANTOM[" + std::to_string(i) + "]", phantoms[i]);

add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us*2));
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA[0]", "16.0");
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
add_param("SCAN_PARAMETERS", "RF_FA", "16.0");
add_param("SCAN_PARAMETERS", "RF_PH", "0");
add_param("SCAN_PARAMETERS", "RF_T", "0");

add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));
add_param("SCAN_PARAMETERS", "DUMMY_SCAN", "-1");
add_param("SCAN_PARAMETERS", "PHASE_CYCLING", "180");
Expand Down
3 changes: 2 additions & 1 deletion src/definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

#define VERSION_MAJOR 1
#define VERSION_MINOR 18
#define VERSION_PATCH 3
#define VERSION_PATCH 4


// Helper macros to stringify values
#define STRINGIFY_HELPER(x) #x
Expand Down
Loading

0 comments on commit 3174590

Please sign in to comment.