-
Notifications
You must be signed in to change notification settings - Fork 32
MaRGE_Bug_Report
Date: March 2026
System: MaRGE on Windows 10, PyPulseq 1.5.0, marcos_client, Red Pitaya (rp-122)
Sequences tested: FID, Larmor, Larmor Raw, Noise, RabiFlops, Shimming, RareDoubleImage
Hardware: SAMuRaI Solenoid coil, ~15.38 MHz (0.36 T)
During hands-on testing of the MaRGE NMR/MRI control software, several bugs were identified and patched across the sequence, reconstruction, and interpreter layers. The most significant issue was a full incompatibility between PyPulseq 1.5.0 (the currently pip-installable version) and the marga_pulseq interpreter, which was written for PyPulseq 1.4. This made the RareDoubleImage sequence completely non-functional. Additional bugs caused crashes in FID, Shimming, and Larmor sequences, and a silent calibration corruption in RabiFlops that affected all subsequent sequences.
All patches have been applied and tested. A new minimal SpinEchoImage sequence was also contributed as a simpler imaging reference.
File: marge/seq/fid.py
Error: ERROR: An error occurred in sequenceAnalysis method: attempt to get argmin of an empty sequence
Root cause: When the acquired signal was too weak or noisy, the spectrum contained no identifiable peak. getFWHM called np.argmin and np.argmax directly on the result without checking for empty or flat arrays.
Fix: Added empty-array guards before every np.argmin / np.argmax call in getFWHM, returning np.nan on failure. Added a ValueError guard at the top of sequenceAnalysis to catch empty data before processing begins.
Great!
Files: marge/seq/mriBlankSeq.py, marge/seq/larmor.py
Error: After running Larmor, the stored Larmor frequency became a number in the hundreds of trillions (Hz instead of MHz), breaking all subsequent sequences.
Root cause: Double unit conversion. sequenceAtributes in mriBlankSeq multiplies larmorFreq by units.MHz = 1e6 (correctly converting the display value in MHz to Hz for internal use and storing it on self.larmorFreq). Then larmor.py::sequenceRun was copying that Hz value back into mapVals['larmorFreq']. The GUI then interpreted this as MHz and displayed and stored the corrupted value.
Fix:
- Added
_sanitize_larmor_mhz(val)helper inmriBlankSeq.pythat detects and corrects values outside the valid MHz range (0.1–100 MHz) - Removed the double-conversion step in
larmor.py::sequenceRun - Added
self.mapVals['bw'] = self.bw * 1e3to correctly store bandwidth in kHz for the reconstruction
I think this is already fixed in newest version
File: marge/recon/Larmor.py
Error: RF excitation amplitude: 0.0000 a.u. printed in recon output regardless of settings.
Root cause: The amplitude formula rfExFA * pi/180 / (rfExTime * hw.b1Efficiency) had an erroneous extra * 1e6 factor applied to rfExTime. Since rfExTime was already in µs, this inflated the denominator to ~90,000,000 instead of ~90, producing a near-zero result.
Fix: Removed the erroneous * 1e6 factor. Added a _squeeze_mat() helper for robust .mat field extraction handling various numpy array shapes (1,1), (1,n), scalar. Added a bw_true field (saved in larmor.py, loaded in Larmor.py) to preserve the actual hardware-corrected bandwidth after experiment initialisation.
I find nothing like this in current Larmor recon
File: marge/seq/larmor_raw.py
Error: Larmor Raw sequence did not appear in the GUI sequence dropdown.
Root cause: self.addParameter(key='toMaRGE', val=False) — the flag that controls GUI visibility was set to False.
Fix: Changed val=False to val=True.
This is intentional, as only sequence 100 % compatible with all functionalities are shown by default
File: marge/recon/RabiFlops.py
Error: After running RabiFlops, all subsequent sequences (Larmor, Shimming, RareDoubleImage) silently used a wrong b1Efficiency, producing incorrect RF amplitudes.
Root cause: Line 63 of RabiFlops.py unconditionally overwrote hw.b1Efficiency in memory with the value calculated from the Rabi fit. This happened every time reconstruction ran, even if the user had already set a calibrated value manually. The hw module is a global singleton shared across all sequences in the session.
Fix: Removed the automatic hw.b1Efficiency = ... assignment. The reconstruction now prints a recommendation message showing the fitted value, leaving the user in control of when and whether to apply it. The recommended workflow is to manually update hw_config.py and hw_rf.csv after verifying the Rabi result.
This is intentional as this sequences is used for autocalibration
Files: marge/seq/shimmingSweep.py, marge/recon/Shimming.py
Error: TypeError: setting an array element with a sequence when writing the optimal shimming value back to self.shimming0.
Root cause: sxVector[np.argmax(dataFFT)] returned a 1-element (1,) array (because sxVector had shape (N,1)). Assigning this to self.shimming0[0] (a scalar slot in a numpy array) raised the error.
Fix: Changed to float(np.squeeze(sxVector[np.argmax(dataFFT)])) to force a scalar before assignment.
Error: Same TypeError during reconstruction, in getFWHM.
Root cause: mat_data['bw'] loaded from .mat as a (1,1) 2D array. Passing it to np.linspace produced a 3D f_vector, causing np.argmax to fail.
Fix: Squeezed to scalar before use: bw = float(np.squeeze(mat_data['bw'])) * 1e-3.
File: marge/recon/data_processing.py
Error: Various ValueError / AttributeError crashes when loading .mat files with certain numpy array shapes for seqName.
Root cause: seqName in .mat files can have shapes (1,1), (1,), object dtype etc. depending on how MATLAB/scipy saved it. Direct .item() calls failed on non-scalar arrays.
Fix: Wrapped extraction in try/except using str(np.squeeze(seq_raw).item()).strip(). Function now returns a safe ({}, [], {}) tuple on any failure instead of propagating an exception to the GUI.
Files: marge/seq/rare_double_image.py, .venv/Lib/site-packages/marga_pulseq/interpreter.py
This was the most critical issue. PyPulseq 1.5.0 (the current version on PyPI) changed the .seq file format in several incompatible ways. The marga_pulseq interpreter was written for PyPulseq 1.4 and failed to parse any sequence generated by 1.5. RareDoubleImage was completely non-functional.
Error: invalid literal for int() with base 10: '' immediately on sequence run.
Root cause: Numeric parameters (etl, nScans, nPoints, add_rd_points, etc.) could arrive as empty strings when the GUI initialises parameter fields before the user enters values.
Fix: Added _to_int(v, default) and _to_float(v, default) coercion helpers at the top of sequenceRun, applied to all numeric parameters.
Error: int('') crash inside the interpreter version parser.
Root cause: PyPulseq 1.5's pp.Sequence sets version fields by parsing __version__. If the version string is malformed, version_revision is set to an empty string ''. The interpreter then calls int('') when reading the [VERSION] section.
Fix: After every pp.Sequence(system) call in rare_double_image.py, explicitly set:
batch.version_major = 1
batch.version_minor = 4
batch.version_revision = 0Error: Interpreter used v1 (legacy) readers for all content, failing to parse v1.5 format sections.
Root cause: interpreter.py only activated v2 readers when self._version_minor == 4. PyPulseq 1.5 writes version_minor = 5, so the condition was never met.
Fix: Changed if self._version_minor == 4: to if self._version_minor >= 4:.
Problem: Multiple places in the interpreter called bare int(x) on values that could be None or empty strings from malformed or newer-format .seq sections.
Fix: Added _safe_int(x, default=0) class method to the interpreter:
def _safe_int(self, x, default=0):
if x is None or (isinstance(x, str) and x.strip() == ''):
return default
return int(float(x))Applied to all version field and block field parsing.
Error: Frequency offset of ADC event 1 (0.0) doesn't match that of RF event 1 (15.0)
Root cause: _read_rf_events_v2 expected 8 fields per RF line. PyPulseq 1.5 produces 12 fields, shifting the positions of delay (now index 6), freq (now index 9), and phase (now index 10). The old reader mapped them to wrong fields, producing zero frequency offsets.
Fix: Added len(tmp) >= 12 branch with correct field remapping. Kept len(tmp) >= 8 as fallback for v1.4 files.
Root cause: _read_adc_events expected 6 fields. PyPulseq 1.5 produces 9, shifting freq (now index 6) and phase (now index 7). Old reader read wrong values into these fields.
Fix: Added len(tmp) >= 9 branch with correct remapping. Kept len(tmp) >= 6 fallback.
Root cause: _read_grad_events_v2 expected 5 fields. PyPulseq 1.5 produces 7, shifting shape_id (index 4), time_shape_id (index 5), and delay (index 6).
Fix: Added len(tmp) >= 7 branch with correct remapping. Kept fallbacks for 5-field and 4-field formats.
Root cause: PyPulseq 1.5 changed the SHAPES section from run-length encoded (RLE) to uncompressed format. The v1 shapes reader tried to parse uncompressed data as RLE, reading blank separator lines as repeat counts and crashing on int('').
Fix: Updated _read_shapes to use _safe_int() throughout and detect/handle both compressed (RLE) and uncompressed formats.
Files added:
marge/seq/spin_echo_image.pymarge/recon/SpinEchoImage.py
A minimal 2D spin echo imaging sequence implemented using the direct flo_dict waveform approach (no PyPulseq dependency). Collects one k-space line per TR using a 90°–180° spin echo with frequency encoding (readout gradient) and phase encoding. Appears in the GUI automatically via toMaRGE=True.
Purpose:
- Useful as a teaching and reference implementation of 2D MRI
- Provides a working imaging sequence that bypasses all PyPulseq compatibility issues
- Simpler starting point for users learning the MaRGE sequence framework
| File | Change |
|---|---|
marge/seq/fid.py |
Empty array guards in getFWHM and sequenceAnalysis
|
marge/seq/larmor.py |
Fixed double unit conversion for larmorFreq; added bw kHz storage |
marge/seq/larmor_raw.py |
Set toMaRGE=True to show in GUI |
marge/seq/shimmingSweep.py |
Fixed scalar assignment for optimal shimming value |
marge/seq/mriBlankSeq.py |
Added _sanitize_larmor_mhz() helper |
marge/seq/rare_double_image.py |
Added parameter coercion; fixed PyPulseq version fields |
marge/recon/Larmor.py |
Fixed RF amplitude calculation; added _squeeze_mat(); added bw_true
|
marge/recon/RabiFlops.py |
Removed automatic hw.b1Efficiency overwrite |
marge/recon/Shimming.py |
Fixed scalar bw extraction in getFWHM
|
marge/recon/data_processing.py |
Robust seqName extraction; safe return on failure |
.venv/.../marga_pulseq/interpreter.py |
Full PyPulseq 1.5 compatibility patch (8 sub-fixes) |
marge/seq/spin_echo_image.py |
New: Simple spin echo imaging sequence |
marge/recon/SpinEchoImage.py |
New: Reconstruction for SpinEchoImage |