function DataTable = nappCopyShift(DataTable,varargin) %NAPPCOPYSHIFT Copy energy shift from one XPS region to spectra of the same sequence but other regions. % This function transfers energy shift calibration data (kinetic or binding % energy) from a reference region to spectra of the same sequence iteration in a different % region. It matches spectra based on filename and acquisition timestamp, % enabling consistent energy axis calibration across XPS % measurements of the same sequence (e.g., copying O1s shift to C1s spectra acquired simultaneously). % % This approach ensures that energy calibrations determined from a % reference region (typically C1s or O1s) are consistently applied to % other regions in the same sequence. Works with both spectral data in % binding and in kinetic energy scale and with images - that is can be % used to align all sweeps in one image. Multiple destination rows can % receive the same shift value if acquired simultaneously (one-to-many % copying). % % SYNTAX % ------ % DataTable = NAPPCOPYSHIFT(DataTable) % % DataTable = NAPPCOPYSHIFT(DataTable, tableSelection, 'Name', Value) % % EXAMPLES % -------- % % Copy O1s kinetic energy shift to C1s spectra % DataTable = nappCopyShift(DataTable, ... % 'regionOld', 'O1s', ... % 'regionNew', 'C1s', ... % 'energyScale', 'kinetic'); % % % Copy binding energy shift from N1s to Cl2p on selected rows % selection = contains(DataTable.name, 'polymer_sample'); % DataTable = nappCopyShift(DataTable, selection, ... % 'regionOld', 'N1s', ... % 'regionNew', 'Cl2p', ... % 'energyScale', 'binding'); % % % Copy C1s calibration (binding energy) to all matching O1s spectra % DataTable = nappCopyShift(DataTable, ... % 'regionOld', 'C1s', ... % 'regionNew', 'O1s'); % % INPUT ARGUMENTS % --------------- % DataTable table % Data table containing XPS spectra with energy shift calibration data % and time stamps. Ideally the napp standard format, but must at least contain: % - name field: spectrum identifiers with region designation (e.g., % 'sample_C1s', 'sample_O1s') % - lastModifiedDatenum field: acquisition timestamps (MATLAB datenum format) % - kineticEnergyShift or bindingEnergyShift field: calibration shift values % (created by nappDetermineShiftXps) % % NAME-VALUE PAIR ARGUMENTS (optional) % ------------------------------------ % 'tableSelection' logical array (default: all rows) % Logical index to filter which rows in DataTable contain the source % shift data (regionOld spectra). Only spectra in this selection will % be processed as source regions. Their shifts will be copied to matching % spectra in regionNew (which may or may not be in tableSelection). % Example: contains(DataTable.name, 'C1s') to use only C1s as source % % 'regionOld' char vector (default: 'O1s') % Source region containing the energy shift calibration data. % Must be one of: % - 'O1s' % - 'C1s' % - 'N1s' % % 'regionNew' char vector (default: 'C1s') % Destination region to which shift data will be copied. % Must be one of: % - 'O1s' % - 'C1s' % - 'N1s' % - 'Cl2p' % % 'energyScale' char vector (default: 'kinetic') % Energy scale type for shift calibration: % - 'kinetic': copies kineticEnergyShift column (used for image analysis) % - 'binding': copies bindingEnergyShift column (used for spectral analysis) % % OUTPUT ARGUMENTS % ---------------- % DataTable table % Updated data table with energy shift values copied to destination region % spectra. The appropriate column is populated: % - kineticEnergyShift (if energyScale = 'kinetic') % - bindingEnergyShift (if energyScale = 'binding') % % Shift values are matched and copied for all spectra in the destination % region (regionNew) that share the same acquisition timestamp as spectra % in the source region (regionOld, filtered by tableSelection). % % MATCHING ALGORITHM % ------------------ % The function matches source spectra (regionOld) to destination spectra % (regionNew) using: % 1. Filename: region name is replaced (O1s → C1s, etc.) % 2. Acquisition timestamp: year, month, day, hour, and minute must all match % % This ensures that shift calibrations from simultaneously acquired reference % regions are correctly applied to their complementary spectra. An error is % displayed if a matching spectrum cannot be found. % % NOTES % ----- % - PREREQUISITE: Run nappDetermineShiftXps on the source region (regionOld) % BEFORE running this function. This populates the shift column in DataTable. % - Timestamp matching is strict: year, month, day, hour, and minute must all % match exactly. Second-level precision is not used. % - If regionOld and regionNew are the same, no copy operation is performed. % - Timestamps with mismatches indicate possible data organization issues and % trigger a diagnostic message. % - The function operates on selected rows only when tableSelection is provided. % Rows outside the selection are not processed as source regions. % % WORKFLOW EXAMPLE % ---------------- % % Typical usage sequence for multi-region XPS dataset: % % 1. Determine shift for reference region (C1s) % DataTable = nappDetermineShiftXps(DataTable, ... % contains(DataTable.name, 'C1s'), ... % 'energyScale', 'binding'); % % % 2. Copy C1s shift to complementary regions % DataTable = nappCopyShift(DataTable, ... % contains(DataTable.name, 'C1s'), ... % 'regionOld', 'C1s', ... % 'regionNew', 'O1s', ... % 'energyScale', 'binding'); % % DataTable = nappCopyShift(DataTable, ... % contains(DataTable.name, 'C1s'), ... % 'regionOld', 'C1s', ... % 'regionNew', 'N1s', ... % 'energyScale', 'binding'); % % SEE ALSO % -------- % nappDetermineShiftXps, nappBindingEnergyXps, nappAlignImages, % nappFitGaussXps, datenum % Define conditions and defaults of input % Old region must be also selected in tableSelection! it is kind like % double. defaultRegionOld = 'O1s'; expectedRegionOld = {'O1s','C1s','N1s'}; defaultRegionNew = 'C1s'; expectedRegionNew = {'O1s','C1s','N1s','Cl2p'}; defaultTableSelection = true(size(DataTable,1)); defaultEnergyScale = 'kinetic'; expectedEnergyScale = {'kinetic','binding'}; % Parse input p = inputParser; addRequired(p,'DataTable',@istable); addOptional(p,'tableSelection',defaultTableSelection,@islogical); addParameter(p,'regionOld',defaultRegionOld,... @(x) any(validatestring(x,expectedRegionOld))); addParameter(p,'regionNew',defaultRegionNew,... @(x) any(validatestring(x,expectedRegionNew))); addParameter(p,'energyScale',defaultEnergyScale,... @(x) any(validatestring(x,expectedEnergyScale))); parse(p,DataTable,varargin{:}); % The function operates as follows: % 1. For each source spectrum/image (regionOld) in tableSelection: % - Extract the energy shift value (kinetic or binding scale) % - Extract the acquisition timestamp % 2. Generate search criteria by replacing the region name in the source % filename (e.g., "sample_O1s" becomes "sample_C1s") % 3. Search entire DataTable for destination spectra/images (regionNew) that: % - Have the transformed filename % - Match the acquisition timestamp exactly (year, month, day, hour, minute) % 4. Copy the source energy shift value to all matching destination rows if contains(p.Results.energyScale,'kinetic') % repeat this for all columns in input. for ii = 1:size(p.Results.DataTable(p.Results.tableSelection,:),1) % get name and date tmpName = replace(p.Results.DataTable(p.Results.tableSelection,:).name{ii}, ... p.Results.regionOld,p.Results.regionNew); tmpDate = p.Results.DataTable(p.Results.tableSelection,:).lastModifiedDatenum(ii); % select spectra tmpDataSelection = contains(p.Results.DataTable.name,tmpName)... & ismember(year(p.Results.DataTable.lastModifiedDatenum),year(tmpDate))... & ismember(month(p.Results.DataTable.lastModifiedDatenum),month(tmpDate))... & ismember(day(p.Results.DataTable.lastModifiedDatenum),day(tmpDate))... & ismember(hour(p.Results.DataTable.lastModifiedDatenum),hour(tmpDate))... & ismember(minute(p.Results.DataTable.lastModifiedDatenum),minute(tmpDate)); if ~any(tmpDataSelection) == 1 disp('There is something wrong with the data time info') disp(tmpName) end % replace the region in the name and copy the data DataTable.kineticEnergyShift(tmpDataSelection,:) = p.Results.DataTable(p.Results.tableSelection,:).kineticEnergyShift(ii); end elseif contains(p.Results.energyScale,'binding') % repeat this for all columns in input. for ii = 1:size(p.Results.DataTable(p.Results.tableSelection,:),1) % get name and date tmpName = replace(p.Results.DataTable(p.Results.tableSelection,:).name{ii}, ... p.Results.regionOld,p.Results.regionNew); tmpDate = p.Results.DataTable(p.Results.tableSelection,:).lastModifiedDatenum(ii); % select spectra tmpDataSelection = contains(p.Results.DataTable.name,tmpName)... & ismember(year(p.Results.DataTable.lastModifiedDatenum),year(tmpDate))... & ismember(month(p.Results.DataTable.lastModifiedDatenum),month(tmpDate))... & ismember(day(p.Results.DataTable.lastModifiedDatenum),day(tmpDate))... & ismember(hour(p.Results.DataTable.lastModifiedDatenum),hour(tmpDate))... & ismember(minute(p.Results.DataTable.lastModifiedDatenum),minute(tmpDate)); if ~any(tmpDataSelection) == 1 disp('There is something wrong with the data time info') disp(tmpName) end % replace the region in the name and copy the data DataTable.bindingEnergyShift(tmpDataSelection,:) = p.Results.DataTable(p.Results.tableSelection,:).bindingEnergyShift(ii); end end end