Skip to content

Commit 3174590

Browse files
authored
add plot sequence (#23)
* 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
1 parent 4234315 commit 3174590

16 files changed

+374
-278
lines changed

config/config_default.ini

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,38 +34,24 @@ T2[1] = 41
3434
; repetition time in microsecond (integer)
3535
TR = 10e3
3636
; echo time in microsecond (integer)
37-
TE[0] = 5e3
38-
TE[1] = 6e3
39-
TE[2] = 7e3
37+
TE = 5e3 6e3 7e3
4038
; RF Flip angle in degree (float)
41-
RF_FA[0] = 15.0
42-
RF_FA[1] = 0.0
43-
RF_FA[2] = 0.0
39+
RF_FA = 15.0 0.0 0.0
4440
; RF Phase in degree (float). Note PHASE_CYCLING will be added to the phase of the first RF
45-
RF_PH[0] = 0.0
46-
RF_PH[1] = 0.0
47-
RF_PH[2] = 0.0
41+
RF_PH = 0.0 0.0 0.0
4842
; Time to apply RF in microsecond (integer). The first RF start time is always 0.0
49-
RF_T[0] = 0
50-
RF_T[1] = 100e3
51-
RF_T[2] = 200e3
43+
RF_T = 0 100e3 200e3
5244
; 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
53-
DEPHASING[0] =
54-
DEPHASING[1] =
55-
DEPHASING[2] =
45+
DEPHASING =
5646
; Time to apply dephasing in microsecond (integer).
57-
DEPHASING_T[0] =
58-
DEPHASING_T[1] =
59-
DEPHASING_T[2] =
47+
DEPHASING_T =
6048
; Gradient in mT/m for each axis (float). Each sample is active for one TIME_STEP
6149
; GRADIENT_XYZ[x] = 1.0 2.2 1.5
62-
GRADIENT_XYZ[0] =
63-
GRADIENT_XYZ[1] =
64-
GRADIENT_XYZ[2] =
50+
GRADIENT_X =
51+
GRADIENT_Y =
52+
GRADIENT_Z =
6553
; Time to apply gradient in micro-second (integer).
66-
GRADIENT_T[0] =
67-
GRADIENT_T[1] =
68-
GRADIENT_T[2] =
54+
GRADIENT_T =
6955
; time intervals per random-walk in micro-second (integer)
7056
TIME_STEP = 50
7157
; 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.

config/gradient.ini

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,21 @@ T2[0] = 1000e3
1414

1515
[SCAN_PARAMETERS]
1616
TR = 40e3
17-
TE[0] = 20e3
17+
TE = 20e3
1818

19-
RF_FA[0] = 90.0
19+
RF_FA = 90.0
20+
RF_PH = 90.0
21+
RF_T = 0
2022

21-
RF_PH[0] = 90.0
23+
GRADIENT_X = 521.9377
24+
GRADIENT_Y = 0.0
25+
GRADIENT_Z = 0.0
26+
GRADIENT_T = 10e3
2227

23-
RF_T[0] = 0
24-
25-
DEPHASING[0] =
26-
DEPHASING[1] =
27-
DEPHASING[2] =
28-
29-
DEPHASING_T[0] =
30-
DEPHASING_T[1] =
31-
DEPHASING_T[2] =
32-
33-
;
34-
GRADIENT_XYZ[0] = 521.9377 0.0 0.
35-
GRADIENT_XYZ[1] =
36-
GRADIENT_XYZ[2] =
37-
38-
GRADIENT_T[0] = 10e3
39-
GRADIENT_T[1] =
40-
GRADIENT_T[2] =
4128

4229
TIME_STEP = 50
4330

4431
[SIMULATION_PARAMETERS]
4532
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation
4633
NUMBER_OF_SPINS = 1030301
47-
FOV_SCALE[0] = 1.0
34+
SCALE[0] = 1.0

config/gre.ini

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ SEQ_NAME = gre
44

55
[SCAN_PARAMETERS]
66
TR = 40000
7-
TE[0] = 20000
8-
9-
RF_FA[0] = 90.0
10-
11-
RF_PH[0] = 0.0
12-
13-
RF_T[0] = 0
7+
TE = 20000
8+
RF_FA = 90.0
9+
RF_PH = 0.0
10+
RF_T = 0
1411

1512
[SIMULATION_PARAMETERS]
1613
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation

config/se.ini

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@ SEQ_NAME = se
44

55
[SCAN_PARAMETERS]
66
TR = 40000
7-
TE[0] = 20000
7+
TE = 20000
88

9-
RF_FA[0] = 90.0
10-
RF_FA[1] = 180.0
11-
12-
RF_PH[0] = 0.0
13-
RF_PH[1] = 90.0
14-
15-
RF_T[0] = 0
16-
RF_T[1] = 10000
9+
RF_FA = 90.0 180.0
10+
RF_PH = 0.0 90
11+
RF_T = 0 10000
1712

1813
[SIMULATION_PARAMETERS]
1914
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation

config/ssfp.ini

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ SEQ_NAME = ssfp
44

55
[SCAN_PARAMETERS]
66
TR = 10000
7-
TE[0] = 5000
8-
9-
RF_FA[0] = 16.0
10-
11-
RF_PH[0] = 0.0
12-
13-
RF_T[0] = 0
7+
TE = 5000
8+
RF_FA = 16.0
9+
RF_PH = 0.0
10+
RF_T = 0
1411

1512
DUMMY_SCAN = -1
1613
PHASE_CYCLING = 180

config/stimulated_echo.ini

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,14 @@ SEQ_NAME = ste
55

66
[SCAN_PARAMETERS]
77
TR = 120e3
8-
TE[0] = 40e3
9-
TE[1] = 80e3
10-
11-
RF_FA[0] = 90.0
12-
RF_FA[1] = 90.0
13-
RF_FA[2] = 90.0
14-
15-
RF_PH[0] = 0.0
16-
RF_PH[1] = 90.0
17-
RF_PH[2] = 90.0
18-
19-
RF_T[0] = 0
20-
RF_T[1] = 20e3
21-
RF_T[2] = 60e3
22-
23-
DEPHASING[0] = 360
24-
DEPHASING[1] = 360
25-
DEPHASING[2] = 720
26-
DEPHASING[3] = 360
27-
28-
DEPHASING_T[0] = 10e3
29-
DEPHASING_T[1] = 30e3
30-
DEPHASING_T[2] = 50e3
31-
DEPHASING_T[3] = 70e3
8+
TE = 40e3 80e3
9+
10+
RF_FA = 90.0 90.0 90.0
11+
RF_PH = 0.0 90.0 90.0
12+
RF_T = 0 20e3 60e3
13+
14+
DEPHASING = 360 360 720 360
15+
DEPHASING_T = 10e3 30e3 50e3 70e3
3216

3317
[SIMULATION_PARAMETERS]
3418
; use 0 for random seed generation, otherwise use a positive integer to make a reproducible simulation

others/plot_seq.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#!/usr/bin/env python
2+
3+
import os
4+
import sys
5+
import configparser
6+
import numpy as np
7+
import matplotlib.pyplot as plt
8+
from matplotlib.patches import Rectangle
9+
10+
def read_ini(file_path):
11+
config = configparser.ConfigParser()
12+
config.optionxform = str # Preserve case of keys
13+
try:
14+
config_parent_dict = {}
15+
config.read(file_path)
16+
if config.has_section('GENERAL'):
17+
if config.has_option('GENERAL', 'PARENT_CONFIG') and config.get('GENERAL', 'PARENT_CONFIG') != '':
18+
parent_config = config.get('GENERAL', 'PARENT_CONFIG')
19+
parent_config_path = os.path.join(os.path.dirname(file_path), parent_config)
20+
config_parent_dict = read_ini(parent_config_path)
21+
22+
config_dict = {section: dict(config.items(section)) for section in config.sections()}
23+
config_combined_dict = {}
24+
all_keys = config_parent_dict.keys() | config_dict.keys()
25+
for key in all_keys:
26+
if key not in config_parent_dict:
27+
config_parent_dict[key] = {}
28+
if key not in config_dict:
29+
config_dict[key] = {}
30+
config_combined_dict[key] = {**config_parent_dict[key], **config_dict[key]}
31+
32+
except Exception as e:
33+
print(f"Error reading INI file: {e}")
34+
sys.exit(1)
35+
36+
return config_combined_dict
37+
38+
def main():
39+
if len(sys.argv) != 2:
40+
print("Usage: python plot_seq.py <file_path>")
41+
sys.exit(1)
42+
file_path = sys.argv[1]
43+
44+
if os.path.exists(file_path) is False :
45+
print(f"The file '{file_path}' does not exist.")
46+
sys.exit(1)
47+
48+
config = read_ini(file_path)
49+
50+
TR = np.float64(config['SCAN_PARAMETERS']['TR'])
51+
TE = np.fromstring(config['SCAN_PARAMETERS']["TE"], sep=' ')
52+
53+
RF_T = np.fromstring(config['SCAN_PARAMETERS']["RF_T"], sep=' ')
54+
RF_FA = np.fromstring(config['SCAN_PARAMETERS']["RF_FA"], sep=' ')
55+
RF_PH = np.fromstring(config['SCAN_PARAMETERS']["RF_PH"], sep=' ')
56+
57+
GRADIENT_T = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_T"], sep=' ')
58+
GRADIENT_X = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_X"], sep=' ')
59+
GRADIENT_Y = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_Y"], sep=' ')
60+
GRADIENT_Z = np.fromstring(config['SCAN_PARAMETERS']["GRADIENT_Z"], sep=' ')
61+
62+
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 6))
63+
for t, h in zip(RF_T, RF_FA):
64+
ax1.annotate('', xy=(t, h), xytext=(t, 0), arrowprops=dict(arrowstyle="->"))
65+
66+
TE_h = np.max(RF_FA)*0.75
67+
for te in TE:
68+
ax1.add_patch(Rectangle((te - 50, 0), 100, TE_h, color='red'))
69+
70+
ax1.set_xlim(-TR/10, TR)
71+
ax1.set_ylim(0, np.max(RF_FA)*1.1)
72+
ax1.spines['top'].set_visible(False)
73+
ax1.spines['right'].set_visible(False)
74+
ax1.set_ylabel('Flip Angle (deg)')
75+
76+
ax2.plot(GRADIENT_T, GRADIENT_X, label='Gradient X')
77+
ax2.plot(GRADIENT_T, GRADIENT_Y, label='Gradient Y')
78+
ax2.plot(GRADIENT_T, GRADIENT_Z, label='Gradient Z')
79+
max_grad = np.max([np.max(GRADIENT_X), np.max(GRADIENT_Y), np.max(GRADIENT_Z)])
80+
min_grad = np.min([np.min(GRADIENT_X), np.min(GRADIENT_Y), np.min(GRADIENT_Z)])
81+
ax2.set_xlim(-TR/10, TR)
82+
ax2.set_ylim(min_grad*1.1, max_grad*1.1)
83+
ax2.spines['top'].set_visible(False)
84+
ax2.spines['right'].set_visible(False)
85+
ax2.legend()
86+
ax2.set_xlabel('Time (ms)')
87+
ax2.set_ylabel('Gradient Amplitude (mT/m)')
88+
89+
plt.show()
90+
91+
if __name__ == "__main__":
92+
main()

src/config/config_generator.cpp

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,25 @@ bool config_generator::generate_default_config(uint32_t TE_us, uint32_t timestep
4242
// repetition time in microsecond (integer)
4343
ini_parent["SCAN_PARAMETERS"]["TR"] = std::to_string(TE_us + timestep_us);
4444
// echo time in microsecond (integer)
45-
ini_parent["SCAN_PARAMETERS"]["TE[0]"] = std::to_string(TE_us);
45+
ini_parent["SCAN_PARAMETERS"]["TE"] = std::to_string(TE_us);
4646
// RF Flip angle in degree (float)
47-
ini_parent["SCAN_PARAMETERS"]["RF_FA[0]"] = "90.0";
47+
ini_parent["SCAN_PARAMETERS"]["RF_FA"] = "90.0";
4848
// RF Phase in degree (float). Note PHASE_CYCLING will be added to the phase of the first RF
49-
ini_parent["SCAN_PARAMETERS"]["RF_PH[0]"] = "0.0";
49+
ini_parent["SCAN_PARAMETERS"]["RF_PH"] = "0.0";
5050
// Time to apply RF in microsecond (integer). The first RF start time is always 0.0
51-
ini_parent["SCAN_PARAMETERS"]["RF_T[0]"] = "0";
51+
ini_parent["SCAN_PARAMETERS"]["RF_T"] = "0";
5252

5353
// 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
54-
ini_parent["SCAN_PARAMETERS"]["DEPHASING[0]"] = "";
54+
ini_parent["SCAN_PARAMETERS"]["DEPHASING"] = "";
5555
// Time to apply dephasing in microsecond (integer).
56-
ini_parent["SCAN_PARAMETERS"]["DEPHASING_T[0]"] = "";
56+
ini_parent["SCAN_PARAMETERS"]["DEPHASING_T"] = "";
5757
// Gradient in mT/m for each axis (float). Each sample is active for one TIME_STEP
58-
ini_parent["SCAN_PARAMETERS"]["GRADIENT_XYZ[0]"] = "";
58+
ini_parent["SCAN_PARAMETERS"]["GRADIENT_X"] = "";
59+
ini_parent["SCAN_PARAMETERS"]["GRADIENT_Y"] = "";
60+
ini_parent["SCAN_PARAMETERS"]["GRADIENT_Z"] = "";
5961
// Time to apply gradient in micro-second (integer).
60-
ini_parent["SCAN_PARAMETERS"]["GRADIENT_T[0]"] = "";
62+
ini_parent["SCAN_PARAMETERS"]["GRADIENT_T"] = "";
63+
6164
// time intervals per random-walk in micro-second (integer)
6265
ini_parent["SCAN_PARAMETERS"]["TIME_STEP"] = std::to_string(timestep_us);
6366
// 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.
@@ -98,10 +101,11 @@ bool config_generator::generate_gre(uint32_t TE_us, uint32_t timestep_us, std::v
98101
add_param("FILES", "PHANTOM[" + std::to_string(i) + "]", phantoms[i]);
99102

100103
add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us + timestep_us));
101-
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
102-
add_param("SCAN_PARAMETERS", "RF_FA[0]", "90.0");
103-
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
104-
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
104+
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
105+
add_param("SCAN_PARAMETERS", "RF_FA", "90.0");
106+
add_param("SCAN_PARAMETERS", "RF_PH", "0");
107+
add_param("SCAN_PARAMETERS", "RF_T", "0");
108+
105109
add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));
106110

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

126130
add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us + timestep_us));
127-
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
128-
add_param("SCAN_PARAMETERS", "RF_FA[0]", "90.0");
129-
add_param("SCAN_PARAMETERS", "RF_FA[1]", "180.0");
130-
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
131-
add_param("SCAN_PARAMETERS", "RF_PH[1]", "90");
132-
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
133-
add_param("SCAN_PARAMETERS", "RF_T[1]", std::to_string(TE_us/2));
131+
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
132+
add_param("SCAN_PARAMETERS", "RF_FA", "90.0 180.0");
133+
add_param("SCAN_PARAMETERS", "RF_PH", "0 90");
134+
add_param("SCAN_PARAMETERS", "RF_T", "0 " + std::to_string(TE_us/2));
134135
add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));
135136

136137
if(output.empty() == false)
137138
if(write_ini(output) == false)
138139
return false;
139140

141+
140142
return true;
141143
}
142144

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

155157
add_param("SCAN_PARAMETERS", "TR", std::to_string(TE_us*2));
156-
add_param("SCAN_PARAMETERS", "TE[0]", std::to_string(TE_us));
157-
add_param("SCAN_PARAMETERS", "RF_FA[0]", "16.0");
158-
add_param("SCAN_PARAMETERS", "RF_PH[0]", "0");
159-
add_param("SCAN_PARAMETERS", "RF_T[0]", "0");
158+
add_param("SCAN_PARAMETERS", "TE", std::to_string(TE_us));
159+
add_param("SCAN_PARAMETERS", "RF_FA", "16.0");
160+
add_param("SCAN_PARAMETERS", "RF_PH", "0");
161+
add_param("SCAN_PARAMETERS", "RF_T", "0");
162+
160163
add_param("SCAN_PARAMETERS", "TIME_STEP", std::to_string(timestep_us));
161164
add_param("SCAN_PARAMETERS", "DUMMY_SCAN", "-1");
162165
add_param("SCAN_PARAMETERS", "PHASE_CYCLING", "180");

src/definitions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
#define VERSION_MAJOR 1
55
#define VERSION_MINOR 18
6-
#define VERSION_PATCH 3
6+
#define VERSION_PATCH 4
7+
78

89
// Helper macros to stringify values
910
#define STRINGIFY_HELPER(x) #x

0 commit comments

Comments
 (0)