@@ -501,39 +501,137 @@ run_pipeline() {
501501 log_message " Selected file: $( basename " $selected_file " ) "
502502 log_message " Rationale: $selection_rationale "
503503
504- # Assign files based on selection result
505- if [ " $selected_modality " == " T1" ]; then
506- local t1_file=" $selected_file "
504+ # ---------------------------------------------------------------------------
505+ # Assign files based on selection result, with external anatomical T1
506+ # adoption (Unit A) when no in-study T1 is available.
507+ #
508+ # Resolution order (executed once; ANATOMICAL_REFERENCE_T1 is considered
509+ # only when the in-study T1 is absent or reference selection failed):
510+ #
511+ # 1. Reference selection chose T1 → use it directly.
512+ # 2. Reference selection chose FLAIR → look for an in-study T1;
513+ # if none found and ANATOMICAL_REFERENCE_T1 is set, adopt it (below).
514+ # 3. Reference selection failed entirely → if ANATOMICAL_REFERENCE_T1
515+ # is set, adopt it and find the best FLAIR in EXTRACT_DIR; else abort.
516+ #
517+ # When ANATOMICAL_REFERENCE_T1 is unset/empty (the default), none of the
518+ # adoption code below is reachable — behaviour is byte-identical to before.
519+ # ---------------------------------------------------------------------------
520+ local t1_file=" " flair_file=" "
521+ # Tracks whether the external anatomical T1 adoption fired (used when setting
522+ # PIPELINE_REFERENCE_MODALITY below so we don't confuse "T1 selected by
523+ # selection" with "T1 from adoption").
524+ local _external_t1_adopted=" false"
525+
526+ if [ " $selected_modality " = " T1" ]; then
527+ t1_file=" $selected_file "
507528 # Find the best FLAIR for this T1
508- local flair_file=$( select_best_scan " SPACE_FLAIR" " *SPACE_FLAIR*.nii.gz" " $EXTRACT_DIR " " $t1_file " " ${FLAIR_SELECTION_MODE:- registration_optimized} " )
509- elif [ " $selected_modality " == " FLAIR" ]; then
510- local flair_file=" $selected_file "
511- # Find the best T1 for this FLAIR
512- local t1_file=$( select_best_scan " T1" " *T1*.nii.gz" " $EXTRACT_DIR " " $flair_file " " ${T1_SELECTION_MODE:- highest_resolution} " )
529+ flair_file=$( select_best_scan " SPACE_FLAIR" " *SPACE_FLAIR*.nii.gz" " $EXTRACT_DIR " " $t1_file " " ${FLAIR_SELECTION_MODE:- registration_optimized} " )
530+ elif [ " $selected_modality " = " FLAIR" ]; then
531+ flair_file=" $selected_file "
532+ # Find the best in-study T1 for this FLAIR
533+ t1_file=$( select_best_scan " T1" " *T1*.nii.gz" " $EXTRACT_DIR " " $flair_file " " ${T1_SELECTION_MODE:- highest_resolution} " )
534+ # If no in-study T1, fall through to the adoption block below.
513535 else
514- log_error " Reference space selection failed: $selection_rationale " $ERR_DATA_MISSING
515- return $ERR_DATA_MISSING
536+ # Reference selection returned neither T1 nor FLAIR (or failed). If
537+ # ANATOMICAL_REFERENCE_T1 is supplied we can still proceed; otherwise abort.
538+ if [ -z " ${ANATOMICAL_REFERENCE_T1:- } " ]; then
539+ log_error " Reference space selection failed and ANATOMICAL_REFERENCE_T1 is not set: $selection_rationale " $ERR_DATA_MISSING
540+ return $ERR_DATA_MISSING
541+ fi
542+ log_formatted " WARNING" " Reference space selection found no usable reference: $selection_rationale — will attempt adoption of ANATOMICAL_REFERENCE_T1"
543+ # t1_file remains empty; the adoption block below sets it.
544+ # Discover FLAIR independently (no T1 anchor available yet).
545+ flair_file=$( select_best_scan " SPACE_FLAIR" " *SPACE_FLAIR*.nii.gz" " $EXTRACT_DIR " " " " ${FLAIR_SELECTION_MODE:- registration_optimized} " )
516546 fi
517-
518- # Set global variables to track the authoritative reference space decision
519- if [ " $selected_modality " == " T1" ]; then
547+
548+ # ---------------------------------------------------------------------------
549+ # External anatomical T1 adoption (Unit A — borrowed/external anchor)
550+ # ---------------------------------------------------------------------------
551+ # Fires when no in-study T1 was found AND ANATOMICAL_REFERENCE_T1 is a
552+ # non-empty path to an existing file.
553+ # The external T1 is COPIED into EXTRACT_DIR under a fixed name so that all
554+ # downstream discovery treats it like a normal in-study T1.
555+ # The copy is refreshed when ANATOMICAL_REFERENCE_T1 changes between runs
556+ # (detected by byte-size mismatch against the destination), so a changed
557+ # external reference is never silently ignored on resume.
558+ # Provenance is written once per run directory (overwrite = idempotent).
559+ # When ANATOMICAL_REFERENCE_T1 is unset/empty this block is a complete no-op.
560+ # ---------------------------------------------------------------------------
561+ if [ -z " ${t1_file:- } " ] && [ -n " ${ANATOMICAL_REFERENCE_T1:- } " ]; then
562+ if [ ! -f " ${ANATOMICAL_REFERENCE_T1} " ]; then
563+ log_error " ANATOMICAL_REFERENCE_T1 set but file not found: ${ANATOMICAL_REFERENCE_T1} " $ERR_DATA_MISSING
564+ return $ERR_DATA_MISSING
565+ fi
566+ # Require .nii.gz input — raw .nii would be copied byte-for-byte into a
567+ # .nii.gz-named destination, producing an invalid compressed file that FSL
568+ # and NIfTI readers would reject or misparse.
569+ if [[ " ${ANATOMICAL_REFERENCE_T1} " != * .nii.gz ]]; then
570+ log_error " ANATOMICAL_REFERENCE_T1 must be a compressed NIfTI (.nii.gz); got: ${ANATOMICAL_REFERENCE_T1} " $ERR_DATA_MISSING
571+ return $ERR_DATA_MISSING
572+ fi
573+ local adopted_t1=" ${EXTRACT_DIR} /external_anatomical_T1.nii.gz"
574+ local ref_label=" ${ANATOMICAL_REFERENCE_LABEL:- ${ANATOMICAL_REFERENCE_T1} } "
575+ log_formatted " WARNING" " No in-study T1 found; adopting EXTERNAL anatomical T1 reference: ${ref_label} "
576+ # Refresh the copy when source and destination sizes differ (covers both the
577+ # first run and a changed ANATOMICAL_REFERENCE_T1 on a subsequent resume).
578+ local _src_size _dst_size
579+ _src_size=$( wc -c < " ${ANATOMICAL_REFERENCE_T1} " 2> /dev/null || echo 0)
580+ _dst_size=$( [ -f " ${adopted_t1} " ] && wc -c < " ${adopted_t1} " 2> /dev/null || echo 0)
581+ if [ " ${_src_size} " != " ${_dst_size} " ] || [ ! -f " ${adopted_t1} " ]; then
582+ log_message " Copying external anatomical T1 into extract dir (source size: ${_src_size} bytes)"
583+ if ! cp " ${ANATOMICAL_REFERENCE_T1} " " ${adopted_t1} " ; then
584+ log_error " Failed to copy external anatomical T1 reference into EXTRACT_DIR" $ERR_DATA_MISSING
585+ return $ERR_DATA_MISSING
586+ fi
587+ else
588+ log_message " External anatomical T1 already present in extract dir (size matches; skipping copy)"
589+ fi
590+ # Sanity-check the adopted file: must exist and be non-empty.
591+ local _adopted_size
592+ _adopted_size=$( wc -c < " ${adopted_t1} " 2> /dev/null || echo 0)
593+ if [ ! -f " ${adopted_t1} " ] || [ " ${_adopted_size} " -eq 0 ]; then
594+ log_error " Adopted external anatomical T1 is missing or empty: ${adopted_t1} " $ERR_DATA_MISSING
595+ return $ERR_DATA_MISSING
596+ fi
597+ t1_file=" ${adopted_t1} "
598+ _external_t1_adopted=" true"
599+ # Record provenance (overwrite = idempotent across resumes).
600+ printf ' source=%s\nlabel=%s\ntype=external\nadopted_as=%s\n' \
601+ " ${ANATOMICAL_REFERENCE_T1} " " ${ref_label} " " ${adopted_t1} " \
602+ > " ${RESULTS_DIR} /anatomical_reference_provenance.txt"
603+ log_message " External anatomical T1 adopted as: ${adopted_t1} "
604+ log_message " Provenance recorded in: ${RESULTS_DIR} /anatomical_reference_provenance.txt"
605+ fi
606+ # ---------------------------------------------------------------------------
607+
608+ # Set global variables to track the authoritative reference space decision.
609+ # The external T1 is always the fixed anatomical anchor when adoption fired
610+ # (PIPELINE_REFERENCE_MODALITY=T1, FLAIR registers to it). In the normal
611+ # path the selection result drives the choice exactly as before.
612+ if [ " $_external_t1_adopted " = " true" ]; then
613+ # The external T1 is the fixed anchor; FLAIR registers TO it.
614+ export PIPELINE_REFERENCE_MODALITY=" T1"
615+ export PIPELINE_REFERENCE_FILE=" ${t1_file} "
616+ export PIPELINE_MOVING_FILE=" ${flair_file:- } "
617+ elif [ " $selected_modality " = " T1" ]; then
520618 export PIPELINE_REFERENCE_MODALITY=" T1"
521619 export PIPELINE_REFERENCE_FILE=" $t1_file "
522- export PIPELINE_MOVING_FILE=" $flair_file "
620+ export PIPELINE_MOVING_FILE=" ${ flair_file:- } "
523621 else
524622 export PIPELINE_REFERENCE_MODALITY=" FLAIR"
525- export PIPELINE_REFERENCE_FILE=" $flair_file "
526- export PIPELINE_MOVING_FILE=" $t1_file "
623+ export PIPELINE_REFERENCE_FILE=" ${ flair_file:- } "
624+ export PIPELINE_MOVING_FILE=" ${ t1_file:- } "
527625 fi
528-
626+
529627 log_formatted " INFO" " ===== Authoritative Reference Space Set ====="
530- log_message " PIPELINE_REFERENCE_MODALITY: $PIPELINE_REFERENCE_MODALITY "
531- log_message " PIPELINE_REFERENCE_FILE: $( basename " $PIPELINE_REFERENCE_FILE " ) "
532- log_message " PIPELINE_MOVING_FILE: $( basename " $PIPELINE_MOVING_FILE " ) "
628+ log_message " PIPELINE_REFERENCE_MODALITY: ${ PIPELINE_REFERENCE_MODALITY:- } "
629+ log_message " PIPELINE_REFERENCE_FILE: $( basename " ${ PIPELINE_REFERENCE_FILE:- <unset>} " ) "
630+ log_message " PIPELINE_MOVING_FILE: $( basename " ${ PIPELINE_MOVING_FILE:- <unset>} " ) "
533631 log_message " ==========================================="
534632
535633 # Log detailed resolution information about selected scans
536- if [ -n " $t1_file " ] && [ -n " $flair_file " ]; then
634+ if [ -n " ${ t1_file:- } " ] && [ -n " ${ flair_file:- } " ]; then
537635 log_message " ======== Selected Scan Information ========"
538636 log_message " T1 scan: $t1_file "
539637 log_message " T1 dimensions: $( fslinfo " $t1_file " | grep -E " ^dim[1-3]" | awk ' {print $1 "=" $2}' | tr ' \n' ' ' ) "
@@ -546,13 +644,17 @@ run_pipeline() {
546644 log_message " Resolution comparison: $( calculate_pixdim_similarity " $t1_file " " $flair_file " ) /100"
547645 log_message " ======================================="
548646 fi
549-
550- if [ -z " $t1_file " ]; then
551- log_error " T1 file not found in $EXTRACT_DIR " $ERR_DATA_MISSING
647+
648+ if [ -z " ${t1_file:- } " ]; then
649+ if [ -n " ${ANATOMICAL_REFERENCE_T1:- } " ]; then
650+ log_error " No in-study T1 found and ANATOMICAL_REFERENCE_T1 is set but adoption did not succeed" $ERR_DATA_MISSING
651+ else
652+ log_error " No in-study T1 found in ${EXTRACT_DIR} and ANATOMICAL_REFERENCE_T1 is not set" $ERR_DATA_MISSING
653+ fi
552654 return $ERR_DATA_MISSING
553655 fi
554-
555- if [ -z " $flair_file " ]; then
656+
657+ if [ -z " ${ flair_file:- } " ]; then
556658 log_error " FLAIR file not found in $EXTRACT_DIR " $ERR_DATA_MISSING
557659 return $ERR_DATA_MISSING
558660 fi
0 commit comments