Updated to new PShell file structure and added abiltiy to read several spectra in each h5 file

This commit is contained in:
bartels-rausch committed 2025-11-21 19:51:44 +01:00
1 parent 723714c85d
commit efff7c29f5
1 file changed
+359 -60
+359 -60
View File
@@ -78,6 +78,8 @@ function ListFiles = nappFileList(dataPath,varargin)
% * April 2021, X-ray kinetic energy and images are now made unique
% * March 2022, input now using varargin
% * March 2022, strict data structure required for local and remote sources
% * November 2025, PShell h5 files for NEXAFS have a new structure; added
% creation date to mac for NEXAFS.
%% Input
% Define conditions and defaults of input
@@ -243,6 +245,78 @@ else
% The second line of code filters all files that start with Numbers or
% letters. All system files starting with a . or ~ are discarded.
% =========================================================================
% SPLIT HDF5 FILES WITH MULTIPLE DATASETS INTO INDIVIDUAL FILES
% =========================================================================
%
% BACKGROUND:
% HDF5 files from the beamline can contain multiple experimental scans
% (e.g., scan_1, scan_2, scan_3) stored as separate groups within a
% single file. However, the NEXAFS and XPS data processing pipelines
% are designed to work with one scan per file.
%
% PURPOSE:
% This script identifies HDF5 files that contain multiple scans and
% splits each scan into a separate file with a descriptive name.
% Each output file contains only one scan (renamed to scan_1) plus
% the logs and metadata groups.
%
% PROCESS:
% 1. Loop through all NEXAFS HDF5 files in the file list
% 2. Check if each file contains more than 3 groups (indicating multiple scans)
% 3. If it does, split it using the splitH5Scans function
% 4. The original file is deleted after splitting
%
% OUTPUT:
% For each multi-scan file, creates separate output files:
% Example: 0002_..._O_Kedge.h5  0002_..._O_Kedge_scan_1.h5
% 0002_..._O_Kedge_scan_2.h5
% =========================================================================
% Loop through each file in the NEXAFS file list
% TmpFileList{2} contains the full paths to all NEXAFS HDF5 files
% ii is the loop counter (1, 2, 3, ... up to the total number of files)
for ii = 1:size(TmpFileList{2}, 1)
% Get the filename of the current file being processed
current_file = [TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name];
% Read information about the structure of the HDF5 file
% This tells us what groups (datasets) are stored in the file
fileinfo = h5info(current_file);
% Extract the names of all groups at the root level
% Example: {'/scan_1', '/scan_2', '/logs', '/__DATA_TYPES__'}
groupnames = {fileinfo.Groups.Name};
% Check if this file has more than 3 groups
% Files with more than 3 groups likely contain multiple scans
% (the extra groups beyond scans are typically '/logs' and '/__DATA_TYPES__')
if numel(groupnames) > 3
% This file contains multiple scans, so split it
% The splitH5Scans function will:
% - Create separate files for each scan
% - Rename each scan to '/scan_1' within its new file
% - Delete the original multi-scan file when done
splitH5Scans(current_file);
end
end
% =========================================================================
% NOTE: After this loop completes, all NEXAFS files in data folder
% will contain exactly one scan per file, ready for processing
% =========================================================================
% EPICS files in h5 format list all files, then get rid of system files
TmpFileList{2} = dir([dataPath_NEXAFS filesep '**' filesep '*.h5']);
TmpFileList{2} = TmpFileList{2}(find(~cellfun(@isempty,regexp({TmpFileList{2}.name},'^[a-zA-Z0-9]\w*')))); %#ok<FNDSB>
disp('*************************************************')
disp('* Voila: Splitting NEXAFS files done *')
disp('*************************************************')
% only process new files
if (istable(p.Results.DataTable) & matches(p.Results.mode,'localNew')) % do this only if previous list was input to function call
@@ -282,20 +356,21 @@ end
try
if contains(p.Results.mode,'data')
tmp.fileinfo = dir([dataPath_XPS '/*.xy']);
filedate_first = tmp.fileinfo(1).datenum;
clearvars name tmp
tmp.fileinfo = dir([dataPath_XPS '/*.xy']);
filedate_first = tmp.fileinfo(1).datenum;
clearvars name tmp
else
tmp.fileinfo = dir([dataPath_XPS '/*.ibw']);
filedate_first = tmp.fileinfo(1).datenum;
clearvars name tmp
tmp.fileinfo = dir([dataPath_XPS '/*.ibw']);
filedate_first = tmp.fileinfo(1).datenum;
clearvars name tmp
end
catch
catch
warning('There appears to be something wrong with the SES or Specs file.')
disp('There appears to be something wrong with the SES or Specs file.')
end
%% Import XPS data to get SES settings
%
% Uses the IgorPro importer for Matlab:
@@ -341,14 +416,14 @@ if contains(p.Results.mode,'data') % for Specs xy data %TODO this is based on fi
disp('Problem reading the Specs file')
fprintf('Number %.0f: %s \n',ii,[TmpFileList{1}(ii).folder filesep TmpFileList{1}(ii).name])
end
try
%% Convert Specs meta data into variables
% data
tmpEndFirstSpectrum = find(diff(tmpSpecsData{1})<0,1); % FIXME: imports only first spectrum
tmpEndFirstSpectrum = find(diff(tmpSpecsData{1})<0,1); % FIXME: imports only first spectrum
% counts
PE.y = tmpSpecsData{2}(1:tmpEndFirstSpectrum);
% kinetic energy
@@ -563,18 +638,28 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
%if ~isempty(h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/energy'))
% creation date of file
TmpFileList{2}(ii).creationDate = datevec(NaN);
if ismac
tmpCommand = sprintf('stat -f "%%SB" "%s"', [TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name]);
[~,creation] = system(tmpCommand);
TmpFileList{2}(ii).creationDate = datevec(creation);
else
TmpFileList{2}(ii).creationDate = datevec(NaN);
end
try
% read data
%if datenum(filedate_first) <= datenum('21-Nov-2020 00:01:00') % Date not exaclty certain.
try
TmpImage= h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/spectrum');
%else
TmpImage= h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/spectrum');
catch
try
TmpImage = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/scienta spectrum');
TmpImage= h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/spectrum');
%else
catch
TmpImage = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/detector/data'); % pre-2020 (?) h5 structure
try
TmpImage = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/scienta spectrum');
catch
TmpImage = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/detector/data'); % pre-2020 (?) h5 structure
end
end
end
% 21.8.20 changed from spectrum to scienta spectrum, later
@@ -587,11 +672,16 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
% import Photon Energy (cols)
try
TmpXrayEkin = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/energy'); % X-ray excitation energy; Photon energy in eV
TmpXrayEkin = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/energy'); % X-ray excitation energy; Photon energy in eV
TmpXrayEkin(TmpXrayEkin==0)=[];
catch
TmpXrayEkin = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/PhotonEnergy'); % X-ray excitation energy; Photon energy in eV
TmpXrayEkin(TmpXrayEkin==0)=[];
try
TmpXrayEkin = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/energy'); % X-ray excitation energy; Photon energy in eV
TmpXrayEkin(TmpXrayEkin==0)=[];
catch
TmpXrayEkin = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/PhotonEnergy'); % X-ray excitation energy; Photon energy in eV
TmpXrayEkin(TmpXrayEkin==0)=[];
end
end
@@ -607,35 +697,45 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
TmpFileList{2}(ii).image = TmpImage(:,ind);
% NEXAFS pass energy
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/PassEnergy'); % in eV
TmpFileList{2}(ii).scientaPassEnergy_eV = str2double(tmp(1));
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/PassEnergy'); % in eV
TmpFileList{2}(ii).scientaPassEnergy_eV = str2double(tmp(1,:));
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/PassEnergy'); % in eV
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/PassEnergy'); % in eV
TmpFileList{2}(ii).scientaPassEnergy_eV = str2double(tmp(1));
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/PassEnergy'); % in eV
if tmp(1) == 1
TmpFileList{2}(ii).scientaPassEnergy_eV = 20;
elseif tmp(1) == 2
TmpFileList{2}(ii).scientaPassEnergy_eV = 50;
elseif tmp(1) == 3
TmpFileList{2}(ii).scientaPassEnergy_eV = 100;
elseif tmp(1) == 4
TmpFileList{2}(ii).scientaPassEnergy_eV = 200;
if tmp(1) == 1
TmpFileList{2}(ii).scientaPassEnergy_eV = 20;
elseif tmp(1) == 2
TmpFileList{2}(ii).scientaPassEnergy_eV = 50;
elseif tmp(1) == 3
TmpFileList{2}(ii).scientaPassEnergy_eV = 100;
elseif tmp(1) == 4
TmpFileList{2}(ii).scientaPassEnergy_eV = 200;
end
end
end
% NEXAFS dwell time
if datenum(filedate_first) >= datenum('1-Feb-2020 23:59:00')
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaStepTime')*1000; % in ms
% if datenum(filedate_first) >= datenum('1-Feb-2020 23:59:00')
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/ScientaStepTime')*1000; % in ms
else
catch
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/StepTime')*1000; % in ms
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaStepTime')*1000; % in ms
catch
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/StepTime')*1000; % in ms
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/StepTime')*1000; % in ms
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/ScientaStepTime')*1000;
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/StepTime')*1000; % in ms
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/ScientaStepTime')*1000;
end
end
end
end
@@ -649,23 +749,31 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
% Acquisition mode
try
TmpFileList{2}(ii).scientaAcquisitionMode = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/AcquisitionMode');
TmpFileList{2}(ii).scientaAcquisitionMode = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/AcquisitionMode');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/AcquireMode');
if tmp(1) == 0
TmpFileList{2}(ii).scientaAcquisitionMode = "Swept";
elseif tmp(1) == 1
TmpFileList{2}(ii).scientaAcquisitionMode = "Fixed";
try
TmpFileList{2}(ii).scientaAcquisitionMode = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/AcquisitionMode');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/AcquireMode');
if tmp(1) == 0
TmpFileList{2}(ii).scientaAcquisitionMode = "Swept";
elseif tmp(1) == 1
TmpFileList{2}(ii).scientaAcquisitionMode = "Fixed";
end
clear tmp
end
clear tmp
end
if strcmp(TmpFileList{2}(ii).scientaAcquisitionMode,'Fixed')
% NEXAFS kinetic energy window - fixed mode
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaCenterEnergy'); % in eV
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/ScientaCenterEnergy'); % in eV
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/CenterEnergy'); % in eV
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaCenterEnergy'); % in eV
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/CenterEnergy'); % in eV
end
end
TmpFileList{2}(ii).scientaEkinRange_eV = [double(tmp(1))-TmpFileList{2}(ii).scientaPassEnergy_eV/20 double(tmp(1))+TmpFileList{2}(ii).scientaPassEnergy_eV/20];
@@ -677,19 +785,28 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
else
% NEXAFS kinetic energy window - scan mode
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaLowEnergy'); % in eV
tmp2 = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaHighEnergy');
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/ScientaLowEnergy'); % in eV
tmp2 = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/ScientaHighEnergy');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/LowEnergy'); % in eV
tmp2 = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/HighEnergy');
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaLowEnergy'); % in eV
tmp2 = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/ScientaHighEnergy');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/LowEnergy'); % in eV
tmp2 = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/HighEnergy');
end
end
TmpFileList{2}(ii).scientaEkinRange_eV = [tmp(1) tmp2(1)];
clearvars tmp
try
TmpFileList{2}(ii).scientaEkinStep_eV = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/StepSize');
TmpFileList{2}(ii).scientaEkinStep_eV = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/StepSize');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/StepSize');
TmpFileList{2}(ii).scientaEkinStep_eV = abs(tmp(1));
try
TmpFileList{2}(ii).scientaEkinStep_eV = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/StepSize');
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/StepSize');
TmpFileList{2}(ii).scientaEkinStep_eV = abs(tmp(1));
end
end
TmpFileList{2}(ii).scientaEkin_eV = [TmpFileList{2}(ii).scientaEkinRange_eV(:,1):TmpFileList{2}(ii).scientaEkinStep_eV(1):TmpFileList{2}(ii).scientaEkinRange_eV(:,2)]';
end
@@ -697,8 +814,12 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
clearvars tmp
% read I_0
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/i0');
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/i0');
catch
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/i0');
catch
end
end
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/RM2I0');
@@ -715,22 +836,31 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element
% Lens Mode
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/LensMode');
TmpFileList{2}(ii).scientaLensMode = tmp{1};
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/LensMode');
TmpFileList{2}(ii).scientaLensMode = tmp;
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/LensMode');
TmpFileList{2}(ii).scientaLensMode = num2str(tmp(1));
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/LensMode');
TmpFileList{2}(ii).scientaLensMode = tmp{1};
catch
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/LensMode');
TmpFileList{2}(ii).scientaLensMode = num2str(tmp(1));
end
end
clearvars tmp
try
TmpFileList{2}(ii).scientaRegionIterations = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/NumIterations');
TmpFileList{2}(ii).scientaRegionIterations = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan_1/snaps/NumIterations');
catch
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/NumIterations');
TmpFileList{2}(ii).scientaRegionIterations = double(tmp(1));
TmpFileList{2}(ii).scientaRegionIterations = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/scan 1/attrs/NumIterations');
catch
TmpFileList{2}(ii).scientaRegionIterations = 1;
try
tmp = h5read([TmpFileList{2}(ii).folder filesep TmpFileList{2}(ii).name],'/entry/instrument/NDAttributes/NumIterations');
TmpFileList{2}(ii).scientaRegionIterations = double(tmp(1));
catch
TmpFileList{2}(ii).scientaRegionIterations = 1;
end
end
end
TmpFileList{2}(ii).scientaSequenceIterations = 1;
@@ -894,6 +1024,22 @@ if matches(p.Results.importLabView,'on')
warning('Problem with LabView file')
disp('Problem reading LabView file')
end
try
% Get list of all files in that folder
TmpFileList = dir([dataPath_LabView filesep '**' filesep 'logfile*.txt']);
% import LabView data
LabView = LabView_importData(fullfile(TmpFileList(1).folder,TmpFileList(1).name)); % Overwrite LabView data in workspace, if this is the first import
if size(TmpFileList,1) > 1 % for additional data files, add these to the LabView data structure.
for ii = 2:size(TmpFileList,1)
LabView = LabView_importData(fullfile(TmpFileList(ii).folder,TmpFileList(ii).name),LabView);
end
end
catch
warning('Problem with LabView file')
disp('Problem reading LabView file')
end
end
disp('*************************************************')
disp('* Voila: Done reading LabView data! *')
@@ -1356,4 +1502,157 @@ end
end
function splitH5Scans(infile)
% splitH5Scans - Split an HDF5 file containing multiple scans into separate files
%
% This function takes an HDF5 file with multiple scan groups (scan_1, scan_2, etc.)
% and creates separate files for each scan. Each output file contains only one
% scan (renamed to scan_1) plus the logs group. The original file is deleted after
% successful splitting.
%
% SYNTAX:
% splitH5Scans(infile)
%
% INPUT:
% infile - String containing the full path to the input HDF5 file
% Example: '0002_20251119_145823_O_Kedge_Nov25.h5'
%
% OUTPUT:
% Creates new HDF5 files in the same directory as the input file.
% Each file is named: [original_name]_scan_N.h5
% Example outputs:
% 0002_20251119_145823_O_Kedge_Nov25_scan_1.h5
% 0002_20251119_145823_O_Kedge_Nov25_scan_2.h5
%
% REQUIREMENTS:
% - MATLAB with HDF5 support (R2011a or later)
% - Input file must have more than 3 root-level groups to be processed
%
% EXAMPLE:
% splitH5Scans('0002_20251119_145823_O_Kedge_Nov25.h5');
%
% Author: Perplexity and Thorsten
% Date: November 2025
% Read the structure of the HDF5 file to see what groups it contains
% h5info returns information about groups, datasets, and attributes
fileinfo = h5info(infile);
% Get the original file's modification date before we start
% This will be copied to all new files to preserve the original timestamp
orig_file_info = dir(infile);
orig_mod_datetime = orig_file_info.date; % Get as datetime object
% Extract the names of all groups at the root level of the file
% Example: {'/scan_1', '/scan_2', '/logs', '/__DATA_TYPES__'}
groupnames = {fileinfo.Groups.Name};
% Only process files that have more than 3 groups at the root level
% This ensures we only split files with multiple scans
if numel(groupnames) > 3
% Find which groups are scan groups (contain 'scan_' in their name)
% This returns a logical array: true for scan groups, false otherwise
is_scan = contains(groupnames, 'scan_');
% Extract only the scan group names
% Example: {'/scan_1', '/scan_2'}
scan_names = groupnames(is_scan);
% Split the input filename into its components
% filepath: directory where file is located (e.g., 'C:\Data\')
% base: filename without extension (e.g., '0002_20251119_145823_O_Kedge_Nov25')
% ext: file extension (e.g., '.h5')
[filepath, base, ext] = fileparts(infile);
% If no directory path, use current directory
if isempty(filepath)
filepath = '.';
end
% Extract the file number prefix and rest of the name using pattern matching
% regexp splits '0002_20251119_145823_O_Kedge_Nov25' into:
% prefix = '0002_'
% rest = '20251119_145823_O_Kedge_Nov25'
pieces = regexp(base, '(\d+_)(.*)', 'tokens', 'once');
prefix = pieces{1}; % The number at the start (e.g., '0002_')
rest = pieces{2}; % Everything after the number
% Loop through each scan group and create a separate file for it
for k = 1:numel(scan_names)
% Get the full path of the current scan group
% Example: '/scan_1' or '/scan_2'
scan_group_full = scan_names{k};
% Remove the leading '/' from the scan name for use in filename
% Example: '/scan_2' becomes 'scan_2'
scan_name_clean = regexprep(scan_group_full, '^/', '');
% Build the output filename in the same directory as input file
% Example: 'C:\Data\0002_20251119_145823_O_Kedge_Nov25_scan_2.h5'
outname = fullfile(filepath, sprintf('%s%s_%s%s', prefix, rest, scan_name_clean, ext));
% Copy the entire original file to create the new output file
% We'll modify this copy to keep only one scan
copyfile(infile, outname);
% Open the new file for reading and writing using low-level HDF5 functions
% fid is a file identifier used to reference this file
% 'H5F_ACC_RDWR' means read and write access
fid = H5F.open(outname, 'H5F_ACC_RDWR', 'H5P_DEFAULT');
% Delete all scan groups except the one we want to keep
% Loop through all scan groups in the original file
for j = 1:numel(scan_names)
other_scan = scan_names{j}; % Get the name of another scan group
% If this is not the scan we want to keep, delete it
if j ~= k
try
% H5L.delete removes the link (reference) to the group
% This makes the group inaccessible but doesn't free space
H5L.delete(fid, other_scan, 'H5P_DEFAULT');
catch
% If deletion fails (e.g., group doesn't exist), continue
% The catch block prevents the script from stopping on errors
end
end
end
% Rename the kept scan group to '/scan_1' for consistency
% This way every output file has a group called '/scan_1'
if ~strcmp(scan_group_full, '/scan_1')
% H5L.move renames/moves a group within the file
% Parameters: source_file, old_path, dest_file, new_path, options
H5L.move(fid, scan_group_full, fid, '/scan_1', 'H5P_DEFAULT', 'H5P_DEFAULT');
end
% Close the HDF5 file to save all changes
H5F.close(fid);
% Copy the modification date from the original file to the new file
% Convert MATLAB datenum to timestamp format: YYYYMMDDhhmm.ss
timestamp_str = string(datetime(orig_mod_datetime), 'yyyyMMddHHmm.ss');
if ismac
touch_cmd = sprintf('touch -t %s "%s"', timestamp_str, outname);
system(touch_cmd);
end
% Print confirmation message showing which file was created
% fprintf('Created: %s\n', outname);
end
% After all split files are successfully created, delete the original file
delete(infile);
% fprintf('Deleted original file: %s\n', infile);
else
% If the file has 3 or fewer groups, it doesn't need splitting
% disp('File has three or fewer groups. No processing required.');
end
end