Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ test/integration/*.xprot
test/integration/*.xml
.atom-build.json
#.vscode/
test/nhlbi_integration_tests/test
test/nhlbi_integration_tests/baselines
*.seq
dep-build/
stats.csv
junit.xml
53 changes: 34 additions & 19 deletions apps/gadgetron/connection/HeaderConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace {
std::string raw_header(read_string_from_stream<uint32_t>(stream));

ISMRMRD::IsmrmrdHeader header{};

//GDEBUG_STREAM("HEADER" << raw_header.c_str());
ISMRMRD::deserialize(raw_header.c_str(), header);
//PD
Expand All @@ -57,10 +57,17 @@ namespace {
if (it->name == "tSequenceVariant") {
GDEBUG("Hash bstar found %s found\n", it->value.c_str());
std::string hash_bstar=it->value.c_str();
std::string bstar_folder_traj="/opt/data/bstar_traj/";
// Loop through all .seq files in bstar_folder_traj
for (const auto& entry : std::filesystem::directory_iterator(bstar_folder_traj)) {

// Search for .seq trajectory files in known locations
std::vector<std::string> traj_search_paths = {
"/opt/data/bstar_traj/",
"/opt/nhlbi-integration-test/data/",
"/opt/code/gadgetron_lit/test/nhlbi_integration_tests/data/",
};
bool seq_found = false;
for (const auto& bstar_folder_traj : traj_search_paths) {
if (!std::filesystem::exists(bstar_folder_traj)) continue;
for (const auto& entry : std::filesystem::recursive_directory_iterator(bstar_folder_traj)) {

if (entry.path().extension()==".seq"){
//Find the hash (Last line of the .seq file)
std::ifstream fin;
Expand All @@ -85,48 +92,49 @@ namespace {
}
}

std::string lastLine;
std::string lastLine;
std::getline(fin,lastLine); // Read the current line
fin.close();

// Comparison hash of the data with hash of the .seq file
if(lastLine.find(hash_bstar) != std::string::npos){
GDEBUG_STREAM("Seq file found with hash"<< lastLine);
std::string traj_h5=(bstar_folder_traj +std::string("traj_") + entry.path().stem().string() + std::string(".h5"));

std::string traj_h5=(entry.path().parent_path().string() + std::string("/traj_") + entry.path().stem().string() + std::string(".h5"));
GDEBUG_STREAM("Pulseq trajectory filepath " << traj_h5);

std::string xml_config;
std::string hdf5_in_group="/dataset";
std::shared_ptr<ISMRMRD::Dataset> ismrmrd_dataset= std::shared_ptr<ISMRMRD::Dataset>(new ISMRMRD::Dataset(traj_h5.c_str(), hdf5_in_group.c_str(), false));
ismrmrd_dataset->readHeader(xml_config);
ISMRMRD::IsmrmrdHeader h_traj;

ISMRMRD::deserialize(xml_config.c_str(), h_traj);
// MODIFying Header matrixSize and FOV : Only reconSpace.matrixSize/FOV are correct in the trajectory file
auto factor_0r = float((size_t(round(float(h_traj.encoding.front().reconSpace.matrixSize.x) / 32.0))) * 32.0) / float(size_t(h_traj.encoding.front().reconSpace.matrixSize.x));
auto factor_1r = float((size_t(round(float(h_traj.encoding.front().reconSpace.matrixSize.y) / 32.0))) * 32.0) / float(size_t(h_traj.encoding.front().reconSpace.matrixSize.y));
auto factor_2r = float((size_t(round(float(h_traj.encoding.front().reconSpace.matrixSize.z) / 32.0))) * 32.0) / float(size_t(h_traj.encoding.front().reconSpace.matrixSize.z));
GDEBUG_STREAM("Recon Matrix traj : X " << h_traj.encoding.front().reconSpace.matrixSize.x << " Y " << h_traj.encoding.front().reconSpace.matrixSize.y << " Z " << h_traj.encoding.front().reconSpace.matrixSize.z);
GDEBUG_STREAM("Factor traj : X " << factor_0r << " Y " << factor_1r << " Z " << factor_2r);

factor_0r = factor_0r == 0 ? 1 : factor_0r;
factor_1r = factor_1r == 0 ? 1 : factor_1r;
factor_2r = factor_2r == 0 ? 1 : factor_2r;

GDEBUG_STREAM("Raw Header information :");
GDEBUG_STREAM("Encoded Matrix: X " << header.encoding.front().encodedSpace.matrixSize.x << " Y " << header.encoding.front().encodedSpace.matrixSize.y << " Z " << header.encoding.front().encodedSpace.matrixSize.z);
GDEBUG_STREAM("Recon Matrix: X " << header.encoding.front().reconSpace.matrixSize.x << " Y " << header.encoding.front().reconSpace.matrixSize.y << " Z " << header.encoding.front().reconSpace.matrixSize.z);
GDEBUG_STREAM("Encoded FOV: X " << header.encoding.front().encodedSpace.fieldOfView_mm.x << " Y " << header.encoding.front().encodedSpace.fieldOfView_mm.y << " Z " << header.encoding.front().encodedSpace.fieldOfView_mm.z);
GDEBUG_STREAM("Recon FOV: X " << header.encoding.front().reconSpace.fieldOfView_mm.x << " Y " << header.encoding.front().reconSpace.fieldOfView_mm.y << " Z " << header.encoding.front().reconSpace.fieldOfView_mm.z);
GDEBUG_STREAM("Encoding Limits: Encoded step 1 max " << header.encoding.at(0).encodingLimits.kspace_encoding_step_1.get().maximum << " Encoded step 2 max " << header.encoding.at(0).encodingLimits.kspace_encoding_step_2.get().maximum );

header.encoding.front().encodedSpace.matrixSize.x=size_t(h_traj.encoding.front().reconSpace.matrixSize.x* factor_0r);
header.encoding.front().encodedSpace.matrixSize.y=size_t(h_traj.encoding.front().reconSpace.matrixSize.y* factor_1r);
header.encoding.front().encodedSpace.matrixSize.z=size_t(h_traj.encoding.front().reconSpace.matrixSize.z* factor_2r);
header.encoding.front().encodedSpace.matrixSize.x=size_t(h_traj.encoding.front().reconSpace.matrixSize.x * factor_0r);
header.encoding.front().encodedSpace.matrixSize.y=size_t(h_traj.encoding.front().reconSpace.matrixSize.y * factor_1r);
header.encoding.front().encodedSpace.matrixSize.z=size_t(h_traj.encoding.front().reconSpace.matrixSize.z * factor_2r);

header.encoding.front().reconSpace.matrixSize.x=size_t(h_traj.encoding.front().reconSpace.matrixSize.x* factor_0r);
header.encoding.front().reconSpace.matrixSize.y=size_t(h_traj.encoding.front().reconSpace.matrixSize.y* factor_1r);
header.encoding.front().reconSpace.matrixSize.z=size_t(h_traj.encoding.front().reconSpace.matrixSize.z* factor_2r);
header.encoding.front().reconSpace.matrixSize.x=size_t(h_traj.encoding.front().reconSpace.matrixSize.x * factor_0r);
header.encoding.front().reconSpace.matrixSize.y=size_t(h_traj.encoding.front().reconSpace.matrixSize.y * factor_1r);
header.encoding.front().reconSpace.matrixSize.z=size_t(h_traj.encoding.front().reconSpace.matrixSize.z * factor_2r);

header.encoding.front().encodedSpace.fieldOfView_mm.x=h_traj.encoding.front().reconSpace.fieldOfView_mm.x;
header.encoding.front().encodedSpace.fieldOfView_mm.y=h_traj.encoding.front().reconSpace.fieldOfView_mm.y;
Expand All @@ -138,7 +146,7 @@ namespace {


// Modifying the header encodingLimits :

header.encoding.at(0).encodingLimits.kspace_encoding_step_1.get().maximum =h_traj.encoding.front().encodingLimits.kspace_encoding_step_1.get().maximum;
header.encoding.at(0).encodingLimits.kspace_encoding_step_2.get().maximum =h_traj.encoding.front().encodingLimits.segment.get().maximum;

Expand All @@ -149,13 +157,20 @@ namespace {
GDEBUG_STREAM("Recon FOV: X " << header.encoding.front().reconSpace.fieldOfView_mm.x << " Y " << header.encoding.front().reconSpace.fieldOfView_mm.y << " Z " << header.encoding.front().reconSpace.fieldOfView_mm.z);
GDEBUG_STREAM("Encoding Limits: Encoded step 1 max " << header.encoding.at(0).encodingLimits.kspace_encoding_step_1.get().maximum << " Encoded step 2 max " << header.encoding.at(0).encodingLimits.kspace_encoding_step_2.get().maximum );

seq_found = true;
break;
}
}

}
if (seq_found) break;

}
if (seq_found) break;
}
if (!seq_found) {
GDEBUG_STREAM("Seq file not found in any search path for hash " << hash_bstar);
}
}
}
}
Expand Down
36 changes: 36 additions & 0 deletions test/nhlbi_integration_tests/cases/imoco_vds.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
[dependency.siemens]
data_file = imoco_vds/noise_data.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client]
configuration = default_measurement_dependencies.xml

[reconstruction.siemens]
data_file = imoco_vds/recon_data.h5
measurement = 0
additional_arguments = skip_converstion

[reconstruction.client]
configuration = imoco_recon_vds.xml

[reconstruction.test]
reference_file = imoco_vds/baseline_output.h5
reference_images = imoco_recon_vds.xml/image_0
output_images = imoco_recon_vds.xml/image_0
value_comparison_threshold = 0.01
scale_comparison_threshold = 0.01

[requirements]
system_memory = 8192
gpu_support = 1
gpu_memory = 8192

[tags]
tags = nhlbi,imoco

[nhlbi]
description = iMOCO VDS 3D lung reconstruction
noise_file = imoco_vds/noise_data.h5
baseline_recon_time = 386.0

44 changes: 44 additions & 0 deletions test/nhlbi_integration_tests/cases/mocolr_bSTAR.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[dependency.siemens]
data_file = mocolr_bSTAR/noise_data.h5
measurement = 0
additional_arguments = skip_converstion

[dependency.client]
configuration = default_measurement_dependencies.xml

[reconstruction.siemens]
data_file = mocolr_bSTAR/recon_data.h5
measurement = 0
additional_arguments = skip_converstion

[reconstruction.client]
configuration = pulmonary_echo0.xml

[reconstruction.test.1]
reference_file = mocolr_bSTAR/baseline_output.h5
reference_images = pulmonary_echo0.xml/image_6
output_images = pulmonary_echo0.xml/image_6
value_comparison_threshold = 0.01
scale_comparison_threshold = 0.01

[reconstruction.test.2]
reference_file = mocolr_bSTAR/baseline_output.h5
reference_images = pulmonary_echo0.xml/image_7
output_images = pulmonary_echo0.xml/image_7
value_comparison_threshold = 0.01
scale_comparison_threshold = 0.01

[requirements]
system_memory = 8192
gpu_support = 1
gpu_memory = 8192

[tags]
tags = nhlbi,mocolr,bSTAR,lung

[nhlbi]
description = MOCO Low Rank respiratory resolved pulmonary reconstruction
noise_file = mocolr_bSTAR/noise_data.h5
additional_dataset_0 = mocolr_bSTAR/bstar_450mm_1.20mm_true0_TR2.32ms_rf200_i110_67k_FA40_WASP_self0_fid0_noise0.seq
additional_dataset_1 = mocolr_bSTAR/traj_bstar_450mm_1.20mm_true0_TR2.32ms_rf200_i110_67k_FA40_WASP_self0_fid0_noise0.h5
baseline_recon_time = 179.6
105 changes: 105 additions & 0 deletions test/nhlbi_integration_tests/delete_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Remove an NHLBI integration test case.

Deletes the .cfg file, removes manifest entries, and optionally deletes
the associated Azure Blob Storage data.

Usage:
python delete_test.py imoco_vds
python delete_test.py imoco_vds --keep-data
"""

import argparse
import sys
from pathlib import Path

from get_nhlbi_data import get_container_client, load_manifest, save_manifest

CASES_DIR = Path(__file__).parent / "cases"
BASELINES_DIR = Path(__file__).parent / "baselines"


def main():
parser = argparse.ArgumentParser(
description="Remove an NHLBI integration test",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument('name', help="Test case name to delete")
parser.add_argument('--keep-data', action='store_true',
help="Keep Azure blobs; only remove local .cfg and manifest entries")
parser.add_argument('--yes', '-y', action='store_true',
help="Skip confirmation prompt")

args = parser.parse_args()
name = args.name

cfg_path = CASES_DIR / f"{name}.cfg"
manifest = load_manifest()
test_entries = [e for e in manifest if e.get('test') == name]

if not cfg_path.exists() and not test_entries:
print(f"Error: Test '{name}' not found")
sys.exit(1)

# Show what will be deleted
print(f"Test: {name}")
if cfg_path.exists():
print(f" Config: {cfg_path}")
if test_entries:
print(f" Manifest entries: {len(test_entries)}")
for entry in test_entries:
print(f" - {entry['file']} ({entry.get('type', 'unknown')})")
if not args.keep_data and test_entries:
print(f" Azure blobs: {len(test_entries)} will be deleted")
else:
print(f" Azure blobs: kept")

baseline_dir = BASELINES_DIR / name
if baseline_dir.exists():
print(f" Local baselines: {baseline_dir}")

# Confirm
if not args.yes:
action = "and Azure blobs" if not args.keep_data else "(keeping Azure data)"
response = input(f"\nDelete test '{name}' {action}? [y/N] ").strip().lower()
if response not in ('y', 'yes'):
print("Cancelled.")
sys.exit(0)

# Delete Azure blobs
if not args.keep_data and test_entries:
try:
container_client = get_container_client()
for entry in test_entries:
blob_name = entry['file']
try:
blob_client = container_client.get_blob_client(blob_name)
blob_client.delete_blob()
print(f" Deleted blob: {blob_name}")
except Exception as e:
print(f" Warning: Could not delete blob {blob_name}: {e}")
except Exception as e:
print(f" Warning: Could not connect to Azure: {e}")
print(" Local files will still be removed.")

# Remove from manifest
remaining = [e for e in manifest if e.get('test') != name]
save_manifest(remaining)
print(f" Removed {len(test_entries)} manifest entries")

# Delete .cfg
if cfg_path.exists():
cfg_path.unlink()
print(f" Deleted {cfg_path}")

# Delete local baseline directory
if baseline_dir.exists():
import shutil
shutil.rmtree(baseline_dir)
print(f" Deleted {baseline_dir}")

print(f"\nTest '{name}' removed successfully.")


if __name__ == '__main__':
main()
Loading