Skip to content

Add ivscc_apfrequency operation#2599

Open
MichaelHuth wants to merge 34 commits intomainfrom
feature/2599-ivscc_apfrequency_operation
Open

Add ivscc_apfrequency operation#2599
MichaelHuth wants to merge 34 commits intomainfrom
feature/2599-ivscc_apfrequency_operation

Conversation

@MichaelHuth
Copy link
Collaborator

@MichaelHuth MichaelHuth commented Dec 15, 2025

close #2581

  • Set the opacity of individual traces to 0.2. Leave average trace opacity at 1.
  • adapt xvalues operation to extract also from xvalues meta data
  • add support to plotter for opacity meta data
  • Support fitting the data (use the error bars of the average trace to weight the fit). Consider implementing a new operation that's passed as an optional argument into ivscc_APfrequency.
  • find out how to get automatically determined xOffset constant from e.g. exp_XOffset integrated fit func after fit
  • add argument to ivscc_apfrequency that enables that all experiments/cells are fitted.
  • add argument to preparefit to limit the fitting range. For flexibility and simplicity I suggest to add this through a mask wave in fit2 then.
  • Make the fit parameters accessible with a related operation
  • the fit result may be based on the acquisition/sweep sequence rather than an ordered list of current intensity (x-scale values) -> 3rd avg mode?
  • add a way to expose the number and type of required coefficients to the user. Perhaps a link to the relevant Igor documentation, and/or a pop-up? -> add to help of preparefit
  • Data on the FTP for regression tests: pr-2599/forMichael.zip

close #2628

@MichaelHuth MichaelHuth self-assigned this Dec 15, 2025
@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch 2 times, most recently from 75c4b24 to bd0beed Compare December 17, 2025 18:15
@MichaelHuth
Copy link
Collaborator Author

@timjarsky This is a first version to play around. There are still a few things I have to add, that I discuss farther below.

ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage]) where [] denotes that the parameters are optional.

xaxisOffset and yaxisOffset are strings that can be min, max and none.

General Plotting Behavior

The operation itself returns internally a full plotting specification that is inserted by the formula plotter at the location where the operation appears in the notebook code.

The operation creates only traces that are separated by with. This means preceding and succeeding formulas that are also separated with with go to the same subwindow.

xAxisPercentage and yAxisPercentage are treated as properties for the plot by the plotter. Specifically plot means in this context the subwindow where the traces go. Plot properties from the last formula in the with chain are applied. (i.e. there is currently no gathering of plot properties).

Thus, the plotter applies the 10% for x and y-axis when used like this, where the formula setting the plot properties is last in the chain:

1
with
ivscc_apfrequency(min, min, 10, 10)

but not for this:

ivscc_apfrequency(min, min, 10, 10)
with
1

because 1 does not include any plot properties.

Operation Arguments

Currently:

ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage])

with xaxisOffset and yaxisOffset as min, max, none and xAxisPercentage, yAxisPercentage a number between 0 and 100.

The (later) final arguments should also expose arguments from apfrequency, so it will change to ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, method, level, resultType, normalize, xAxisType).

The default for xaxisOffset and yaxisOffset is min.

On the basis of the experiment avgMethodTesting2.pxp the generated code is:

sel = select(selsweeps(), selstimset("*rheo*", "*supra*"), selvis(all))
selexpAD0 = select(selexp("C57BL6J-734969.15.10A.01.nwb"), $sel, selchannels(AD0), selrange(E1))
selexpDA0 = select(selexp("C57BL6J-734969.15.10A.01.nwb"), $sel, selchannels(DA0), selrange(E1))
freq0 = apfrequency(data($selexpAD0))
current0 = max(data($selexpDA0))
currentNorm0 = $current0 - extract($current0, 0) #*1
selexpAD1 = select(selexp("C57BL6J-734969.15.10B.01.nwb"), $sel, selchannels(AD0), selrange(E1))
selexpDA1 = select(selexp("C57BL6J-734969.15.10B.01.nwb"), $sel, selchannels(DA0), selrange(E1))
freq1 = apfrequency(data($selexpAD1))
current1 = max(data($selexpDA1))
currentNorm1 = $current1 - extract($current1, 0) #*2
ivsccavg = avg([$freq0,$freq1], group)
ivscccurrentavg = avg([$currentNorm0,$currentNorm1], group)

$freq0 - extract($freq0, 0) #*3
vs
$currentNorm0
with
$freq1 - extract($freq1, 0) #*4
vs
$currentNorm1
with
$ivsccavg - extract($ivsccavg, 0) #*5
vs
$ivscccurrentavg - extract($ivscccurrentavg, 0) #*6

I added a #* to the formulas that change depending on the min, max, none setting.

#*1, #*2 and #*6 depend on the xaxisOffset argument with the following logic:

min: currentNormX = $currentX - extract($currentX, 0)
max: currentNormX = $currentX - max(flatten($currentX))
none: currentNormX = $currentX

for #*6 it is:
min: $ivscccurrentavg - extract($ivscccurrentavg, 0)
max: $ivscccurrentavg - max(flatten($ivscccurrentavg))
none: $ivscccurrentavg

#*3, #*4 and #*5 depend on the yaxisOffset argument with the following logic:

min: $freqX - extract($freqX, 0)
max: $freqX - max(flatten($freqX))
none: $freqX

for #*5 it is:
min: $ivsccavg - extract($ivsccavg, 0)
max: $ivsccavg - max(flatten($ivsccavg))
none: $ivsccavg

I need to add a flatten operation because the result in e.g. $freqX are single values in 7 datasets (for avgMethodTesting2.pxp). Thus, our max() operation would determine the max in each dataset individually, but that is not what is wanted. The flatten operation should change n datasets with a single data point to an array with n elements.

Therefore, the max argument is currently not implemented yet until I have the flatten operation implemented.

An additional task from the issue is to add a variable that contains the names of the experiments. I can create this variable in the operation and add it to the variable storage of the formula notebook. It would be available then after the operation ran.
For now I thought of $ivscc_apfrequency_explist as name and it would be a string array.

Base automatically changed from feature/2592-refactor_sf_plotter2 to main December 17, 2025 21:59
@timjarsky
Copy link
Collaborator

@MichaelHuth attempting to use ivscc_apfrequency() on the linked data , resulted in the following assertions:

  !!! Assertion FAILED !!!
  Message: "Input and output must have the same size."
  Please provide the following information if you contact the MIES developers:
  ################################
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Stacktrace:
  SF_button_sweepFormula_display(...)#L2158 [MIES_SweepFormula.ipf]
SF_FormulaPlotter(...)#L1401 [MIES_SweepFormula.ipf]
SF_GatherFormulaResults(...)#L255 [MIES_SweepFormula.ipf]
SFE_ExecuteFormula(...)#L47 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L520 [MIES_SweepFormula_Executor.ipf]
SFO_OperationIVSCCApFrequency(...)#L2570 [MIES_SweepFormula_Operations.ipf]
SFE_ExecuteVariableAssignments(...)#L89 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L348 [MIES_SweepFormula_Executor.ipf]
SFO_OperationAvg(...)#L519 [MIES_SweepFormula_Operations.ipf]
SFH_TransferFormulaDataWaveNoteAndMeta(...)#L974 [MIES_SweepFormula_Helpers.ipf]
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Time: 2025-12-17T12:58:37-08:00
  Locked device: [- none -]
  Current sweep: [- none -]
  DAQ: [- none -]
  Testpulse: [- none -]
  Acquisition state: [- none -]
  Experiment: Untitled ()
  Igor Pro version: 10.0.12.29764 (29764)
  MIES version:
  Release_2.9_20250502-557-gbd0beedf3-dirty
Date and time of last commit: 2025-12-17T19:14:41+01:00
Submodule status: 
-e812ca28993a2414192e48c572085db5f17a13d7 Packages/IPNWB
-5270cb8911ff888ae74f6d3ee2b7b0d49235f98b Packages/doc/doxygen-filter-ipf
-995e48203398ad085f372c0ec6b9f7cbb2bb0362 Packages/igortest
  ################################
  MIES BUG AD_GetSquarePulseFailMsg(...)#L486: Missing DAScale stepsize LBN entry
  !!! Assertion FAILED !!!
  Message: "Input and output must have the same size."
  Please provide the following information if you contact the MIES developers:
  ################################
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Stacktrace:
  AD_CheckProc_Toggle(...)#L1423 [MIES_AnalysisFunctions_Dashboard.ipf]
AdaptDependentControls(...)#L2300 [MIES_GuiUtilities.ipf]
PGC_SetAndActivateControl(...)#L294 [MIES_ProgrammaticGUIControl.ipf]
BSP_CheckProc_OverlaySweeps(...)#L1319 [MIES_BrowserSettingsPanel.ipf]
OVS_UpdatePanel(...)#L271 [MIES_OverlaySweeps.ipf]
OVS_EndIncrementalUpdate(...)#L851 [MIES_OverlaySweeps.ipf]
UpdateSweepPlot(...)#L669 [MIES_MiesUtilities_GUI.ipf]
SB_UpdateSweepPlot(...)#L333 [MIES_AnalysisBrowser_SweepBrowser.ipf]
PostPlotTransformations(...)#L781 [MIES_Browser_Plotter.ipf]
SF_Update(...)#L2107 [MIES_SweepFormula.ipf]
PGC_SetAndActivateControl(...)#L220 [MIES_ProgrammaticGUIControl.ipf]
SF_button_sweepFormula_display(...)#L2158 [MIES_SweepFormula.ipf]
SF_FormulaPlotter(...)#L1401 [MIES_SweepFormula.ipf]
SF_GatherFormulaResults(...)#L255 [MIES_SweepFormula.ipf]
SFE_ExecuteFormula(...)#L47 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L520 [MIES_SweepFormula_Executor.ipf]
SFO_OperationIVSCCApFrequency(...)#L2570 [MIES_SweepFormula_Operations.ipf]
SFE_ExecuteVariableAssignments(...)#L89 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L348 [MIES_SweepFormula_Executor.ipf]
SFO_OperationAvg(...)#L519 [MIES_SweepFormula_Operations.ipf]
SFH_TransferFormulaDataWaveNoteAndMeta(...)#L974 [MIES_SweepFormula_Helpers.ipf]
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Time: 2025-12-17T12:59:11-08:00
  Locked device: [- none -]
  Current sweep: [- none -]
  DAQ: [- none -]
  Testpulse: [- none -]
  Acquisition state: [- none -]
  Experiment: Untitled ()
  Igor Pro version: 10.0.12.29764 (29764)
  MIES version:
  Release_2.9_20250502-557-gbd0beedf3-dirty
Date and time of last commit: 2025-12-17T19:14:41+01:00
Submodule status: 
-e812ca28993a2414192e48c572085db5f17a13d7 Packages/IPNWB
-5270cb8911ff888ae74f6d3ee2b7b0d49235f98b Packages/doc/doxygen-filter-ipf
-995e48203398ad085f372c0ec6b9f7cbb2bb0362 Packages/igortest
  ################################
  ** a wave read gave error: Index out of range for wave "_free_".
  !!! Assertion FAILED !!!
  Message: "Input and output must have the same size."
  Please provide the following information if you contact the MIES developers:
  ################################
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Stacktrace:
  AD_CheckProc_Toggle(...)#L1423 [MIES_AnalysisFunctions_Dashboard.ipf]
AdaptDependentControls(...)#L2285 [MIES_GuiUtilities.ipf]
PGC_SetAndActivateControl(...)#L294 [MIES_ProgrammaticGUIControl.ipf]
BSP_CheckProc_OverlaySweeps(...)#L1319 [MIES_BrowserSettingsPanel.ipf]
OVS_UpdatePanel(...)#L271 [MIES_OverlaySweeps.ipf]
OVS_EndIncrementalUpdate(...)#L851 [MIES_OverlaySweeps.ipf]
UpdateSweepPlot(...)#L669 [MIES_MiesUtilities_GUI.ipf]
SB_UpdateSweepPlot(...)#L333 [MIES_AnalysisBrowser_SweepBrowser.ipf]
PostPlotTransformations(...)#L781 [MIES_Browser_Plotter.ipf]
SF_Update(...)#L2107 [MIES_SweepFormula.ipf]
PGC_SetAndActivateControl(...)#L220 [MIES_ProgrammaticGUIControl.ipf]
SF_button_sweepFormula_display(...)#L2158 [MIES_SweepFormula.ipf]
SF_FormulaPlotter(...)#L1401 [MIES_SweepFormula.ipf]
SF_GatherFormulaResults(...)#L255 [MIES_SweepFormula.ipf]
SFE_ExecuteFormula(...)#L47 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L520 [MIES_SweepFormula_Executor.ipf]
SFO_OperationIVSCCApFrequency(...)#L2570 [MIES_SweepFormula_Operations.ipf]
SFE_ExecuteVariableAssignments(...)#L89 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L348 [MIES_SweepFormula_Executor.ipf]
SFO_OperationAvg(...)#L519 [MIES_SweepFormula_Operations.ipf]
SFH_TransferFormulaDataWaveNoteAndMeta(...)#L974 [MIES_SweepFormula_Helpers.ipf]
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Time: 2025-12-17T12:59:22-08:00
  Locked device: [- none -]
  Current sweep: [- none -]
  DAQ: [- none -]
  Testpulse: [- none -]
  Acquisition state: [- none -]
  Experiment: Untitled ()
  Igor Pro version: 10.0.12.29764 (29764)
  MIES version:
  Release_2.9_20250502-557-gbd0beedf3-dirty
Date and time of last commit: 2025-12-17T19:14:41+01:00
Submodule status: 
-e812ca28993a2414192e48c572085db5f17a13d7 Packages/IPNWB
-5270cb8911ff888ae74f6d3ee2b7b0d49235f98b Packages/doc/doxygen-filter-ipf
-995e48203398ad085f372c0ec6b9f7cbb2bb0362 Packages/igortest
  ################################
  ** a wave read gave error: Index out of range for wave "_free_".
  MIES BUG AD_GetSquarePulseFailMsg(...)#L486: Missing DAScale stepsize LBN entry
  !!! Assertion FAILED !!!
  Message: "Input and output must have the same size."
  Please provide the following information if you contact the MIES developers:
  ################################
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Stacktrace:
  SF_button_sweepFormula_display(...)#L2158 [MIES_SweepFormula.ipf]
SF_FormulaPlotter(...)#L1401 [MIES_SweepFormula.ipf]
SF_GatherFormulaResults(...)#L255 [MIES_SweepFormula.ipf]
SFE_ExecuteFormula(...)#L47 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L520 [MIES_SweepFormula_Executor.ipf]
SFO_OperationIVSCCApFrequency(...)#L2570 [MIES_SweepFormula_Operations.ipf]
SFE_ExecuteVariableAssignments(...)#L89 [MIES_SweepFormula_Executor.ipf]
SFE_FormulaExecutor(...)#L348 [MIES_SweepFormula_Executor.ipf]
SFO_OperationAvg(...)#L519 [MIES_SweepFormula_Operations.ipf]
SFH_TransferFormulaDataWaveNoteAndMeta(...)#L974 [MIES_SweepFormula_Helpers.ipf]
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Time: 2025-12-17T13:04:21-08:00
  Locked device: [- none -]
  Current sweep: [- none -]
  DAQ: [- none -]
  Testpulse: [- none -]
  Acquisition state: [- none -]
  Experiment: Untitled ()
  Igor Pro version: 10.0.12.29764 (29764)
  MIES version:
  Release_2.9_20250502-557-gbd0beedf3-dirty
Date and time of last commit: 2025-12-17T19:14:41+01:00
Submodule status: 
-e812ca28993a2414192e48c572085db5f17a13d7 Packages/IPNWB
-5270cb8911ff888ae74f6d3ee2b7b0d49235f98b Packages/doc/doxygen-filter-ipf
-995e48203398ad085f372c0ec6b9f7cbb2bb0362 Packages/igortest
  ################################
  MIES BUG_TS: Encountered pending RTE: 1321, a wave read;Index out of range for wave "_free_".
  !!! Assertion FAILED !!!
  Message: "Variable not found"
  Please provide the following information if you contact the MIES developers:
  ################################
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Stacktrace:
  SF_button_sweepFormula_display(...)#L2171 [MIES_SweepFormula.ipf]
SF_MarkErrorLocationInNotebook(...)#L2760 [MIES_SweepFormula.ipf]
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Time: 2025-12-17T13:04:26-08:00
  Locked device: [- none -]
  Current sweep: [- none -]
  DAQ: [- none -]
  Testpulse: [- none -]
  Acquisition state: [- none -]
  Experiment: Untitled ()
  Igor Pro version: 10.0.12.29764 (29764)
  MIES version:
  Release_2.9_20250502-557-gbd0beedf3-dirty
Date and time of last commit: 2025-12-17T19:14:41+01:00
Submodule status: 
-e812ca28993a2414192e48c572085db5f17a13d7 Packages/IPNWB
-5270cb8911ff888ae74f6d3ee2b7b0d49235f98b Packages/doc/doxygen-filter-ipf
-995e48203398ad085f372c0ec6b9f7cbb2bb0362 Packages/igortest
  ################################
  ** FindDimLabel gave error: expected wave name
  ** a wave read gave error: Index out of range for wave "_free_".

@MichaelHuth
Copy link
Collaborator Author

I had to fill a request form from Google for the data.

Copilot AI review requested due to automatic review settings December 18, 2025 12:35
@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from bd0beed to d228f41 Compare December 18, 2025 12:35
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new ivscc_apfrequency operation to the sweep formula system, along with supporting infrastructure changes including a flatten operation and refactored plotting code.

Key Changes

  • Adds ivscc_apfrequency operation for analyzing action potential frequency in IV sweep current clamp experiments
  • Introduces flatten helper operation to convert datasets of single values into 1D arrays
  • Refactors plotting infrastructure by introducing SF_PlotterGraphStruct to encapsulate plotting state
  • Adds axis offset and percentage control capabilities to the plotting metadata system

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
Packages/MIES/MIES_Constants.ipf Adds new metadata constants for plot customization and operation names (contains merge conflict)
Packages/MIES/MIES_SweepFormula_Operations.ipf Implements flatten and ivscc_apfrequency operations
Packages/MIES/MIES_SweepFormula_Executor.ipf Registers new operations in the executor switch statement
Packages/MIES/MIES_SweepFormula.ipf Major refactoring of plotting code to use structure-based state management
Packages/MIES/MIES_SweepFormula_Helpers.ipf Adds helper functions for internal formula execution and variable storage
Packages/MIES/MIES_WaveDataFolderGetters.ipf Expands plot metadata wave to include axis offset/percentage properties

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from d228f41 to 5e60339 Compare December 18, 2025 16:59
Copilot AI review requested due to automatic review settings December 19, 2025 15:53
@MichaelHuth
Copy link
Collaborator Author

@timjarsky I fixed the assertion that came up. The reason was that with the three experiments the selections result in 20 sweeps from the first experiment, 24 from the second and 16 from the third. The avg operation was transferring the wave notes and meta data always from the first group to the averaged result. And this failed because the averaged result always has the highest number of datasets (24 in this case) and I can not transfer from 20 datasets to 24 datasets as for the last 4 there would be not wave notes and meta data to transfer.
I changed the behavior of the avg operation in group mode now that it transfers the wave notes and meta data from the group with the highest number of datasets. For this case this means it is transferred from the second group.

For this data this also means that for the first 16 sweeps in the groups the average is over 3 sweeps, 16 - 19 over 2 sweeps and 20 - 23 over 1 sweep.

In the most recent version of this PR also the max argument works now as I added the flatten operation.

And the $ivscc_apfrequency_explist variable is created.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 14 comments.

Comments suppressed due to low confidence (1)

Packages/MIES/MIES_SweepFormula.ipf:1630

  • Missing return statement: The function SF_AddPlotTraceStyle declares a return value of type SF_PlotterGraphStruct but has no explicit return statement. This will cause a compilation error or undefined behavior. Add 'return [pg]' at the end of the function.
static Function [STRUCT SF_PlotterGraphStruct pg] SF_AddPlotTraceStyle(variable formulasAreDifferent)

	variable i, j, numTraces, markerCode, lineCode, isCategoryAxis, tagCounter, lineStyle, overrideMarker, traceToFront
	string trace, info, tagText, name, wvName

	for(i = 0; i < pg.formulaCounter; i += 1)
		WAVE/WAVE plotFormData  = pg.collPlotFormData[i]
		WAVE/T    tracesInGraph = plotFormData[0]
		WAVE/WAVE dataInGraph   = plotFormData[1]
		numTraces  = DimSize(tracesInGraph, ROWS)
		markerCode = formulasAreDifferent ? i : 0
		markerCode = SFH_GetPlotMarkerCodeSelection(markerCode)
		lineCode   = formulasAreDifferent ? i : 0
		lineCode   = SFH_GetPlotLineCodeSelection(lineCode)
		for(j = 0; j < numTraces; j += 1)

			WAVE/Z wvX = dataInGraph[j][%WAVEX]
			WAVE   wvY = dataInGraph[j][%WAVEY]
			trace = tracesInGraph[j]

			info           = AxisInfo(pg.win, "left")
			isCategoryAxis = (NumberByKey("ISCAT", info) == 1)

			if(isCategoryAxis)
				WAVE traceColorHolder = wvX
			else
				WAVE traceColorHolder = wvY
			endif

			WAVE/Z traceColor = JWN_GetNumericWaveFromWaveNote(traceColorHolder, SF_META_TRACECOLOR)
			if(WaveExists(traceColor))
				switch(DimSize(traceColor, ROWS))
					case 3:
						ModifyGraph/W=$pg.win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2])
						break
					case 4:
						ModifyGraph/W=$pg.win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2], traceColor[3])
						break
					default:
						FATAL_ERROR("Invalid size of trace color wave")
				endswitch
			endif

			tagText = JWN_GetStringFromWaveNote(wvY, SF_META_TAG_TEXT)
			if(!IsEmpty(tagText))
				name = "tag" + num2str(tagCounter++)
				Tag/C/N=$name/W=$pg.win/F=0/L=0/X=0.00/Y=0.00 $trace, 0, tagText
			endif

			ModifyGraph/W=$pg.win mode($trace)=SF_DeriveTraceDisplayMode(wvX, wvY)

			lineStyle = JWN_GetNumberFromWaveNote(wvY, SF_META_LINESTYLE)
			if(IsValidTraceLineStyle(lineStyle))
				ModifyGraph/W=$pg.win lStyle($trace)=lineStyle
			elseif(formulasAreDifferent)
				ModifyGraph/W=$pg.win lStyle($trace)=lineCode
			endif

			WAVE/Z customMarkerAsFree = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_MOD_MARKER)
			if(WaveExists(customMarkerAsFree))
				DFREF dfrWork = SFH_GetWorkingDF(pg.graph)
				wvName = "customMarker_" + NameOfWave(wvY)
				WAVE customMarker = MoveFreeWaveToPermanent(customMarkerAsFree, dfrWork, wvName)
				ASSERT(DimSize(wvY, ROWS) == DimSize(customMarker, ROWS), "Marker size mismatch")
				ModifyGraph/W=$pg.win zmrkNum($trace)={customMarker}
			else
				overrideMarker = JWN_GetNumberFromWaveNote(wvY, SF_META_MOD_MARKER)

				if(!IsNaN(overrideMarker))
					markerCode = overrideMarker
				endif

				ModifyGraph/W=$pg.win marker($trace)=markerCode
			endif

			traceToFront = JWN_GetNumberFromWaveNote(wvY, SF_META_TRACETOFRONT)
			traceToFront = IsNaN(traceToFront) ? 0 : !!traceToFront
			if(traceToFront)
				ReorderTraces/W=$pg.win _front_, {$trace}
			endif

		endfor
	endfor
End

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from 0b71dcf to dc98aba Compare December 19, 2025 16:04
@timjarsky
Copy link
Collaborator

@MichaelHuth

Thanks for handling the metadata management for mismatched sweep numbers across experiments.

A few points for discussion:

  1. The marker coloring makes it hard for me to evaluate the output (see plot image below). Can we use a single color for each experiment? Can the data be a single 1D wave instead of multiple x-y waves (this will enable fitting)?

  2. There are many zero-frequency measurements spread across the x-axis.

  3. The average seems more variable than the input data, perhaps because there are too many data points?

  4. I'm not sure about the utility of the negative current values with "min" and "max" (second image). The two axes options that I'm sure are needed are none (where each FI curve starts at zero current and zero frequency) and the absolute current values.

ivscc_apfrequency(none, none)
image

ivscc_apfrequency()
image

Copilot AI review requested due to automatic review settings December 20, 2025 03:25
@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from dc98aba to 79f149a Compare December 20, 2025 03:25
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@MichaelHuth
Copy link
Collaborator Author

MichaelHuth commented Dec 20, 2025

@timjarsky

  1. I changed that. There are now experiment + 1 traces with markers. The +1 trace is the average in the average color. The experiment traces use the common table for trace colors, with one color per experiment. I also added code to show the experiment name in the legend for these.
  2. I still have to look into this, why a lot of the results return a zero. Currently the standard setting of apfrequency for the internal apfrequency call are used.
  3. Yes, it looks a bit like that. I did not see that in my local testing with the three experiments though. I guess in the graphs you pasted there is for some of the average points only a single sweep for an index in the groups.
    e.g. first exp has 10 and second exp has 11 sweeps selected, then sweep data for index 10 is only present from the second experiment -> only one sweep goes into the average and then the trace point from the experiment and the trace point from the average are equal. The average is shown in front and covers the point from the experiment. You may check for the average points on the very top if there is a data point from an experiment underneath.
  4. I think it is the other way around, with none no offset is applied. min moves the trace to zero and max moves the trace such that the former maximum point is at zero.
    What I think might be unexpected with the initial formula construct is that there is an offset used per trace. Thus, when changing none -> min then each trace start is moved to zero separately and the visual relation between the traces shifts. (same applies for max)
    This per-trace behavior is the same for the xaxisOffset and yaxisOffset.
image

@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from 79f149a to 95732dc Compare December 22, 2025 14:16
@MichaelHuth
Copy link
Collaborator Author

@timjarsky
Regarding 2.: The default level for apfrequency is 0. This results in zero peaks found for sweeps where E1 does not cross the zero line and for this apfrequency returns 0 as result with the default arguments.

I have added support for the apfrequency argument block after the first four argument for ivscc_apfrequency. The arguments are now:
ivscc_apfrequency([xaxisOffset, yaxisOffset, xAxisPercentage, yAxisPercentage, method, level, resultType, normalize, xAxisType])

The last four arguments are "forwarded" to apfrequency.

@timjarsky
Copy link
Collaborator

@MichaelHuth, Are failing sweeps included or filtered out? If included, please update to use only passing sweeps.

Copilot AI review requested due to automatic review settings December 22, 2025 19:09
By definition the first NUM_HEADSTAGES colors are used to distinguish data
from headstages. The following color is used for averaged traces.
The new function GetTraceColorNonHeadstage returns a color that is not part of this pool.

Also fixed a typo in the description for GetTraceColorForAverage
…FitFunctionCoefficientNumber

The functions allow to programmatically retrieve information about Igors integrated fit functions.

IsIntegratedFitFunction allows to check a fit function name if it
matches an Igor integrated fit function.

GetIntegratedFitFunctionCoefficientNumber returns the number of coefficients
required by an integrated fit function.

The wave getter GetSFIgorFitProperties can be extended with more information
in additional columns if needed.
This allows to specify fit constraints in the form "K0 > 3" in a sweep formula.
The PrepareFit operation allows to gather information for fitting input data.
The function returns a wave reference wave that stores all gathered information.
The format of this wave is designed to allow further extensions for e.g.
masking, weights etc.

This is a preparation operation for a fit operation that implements the actual fit.
PrepareFit allows to use the same fit setup for different fits.
- this allows the user to use getmeta($ivscc_apfrequency_fit)
Copilot AI review requested due to automatic review settings February 25, 2026 13:28
@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from 396c0a1 to 95edd75 Compare February 25, 2026 13:28
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +674 to +675
WAVE wTmp = output[i]
printf "Bin: %d Avg result: %f, xValue: %f, ySdev: %f, xSdev: %f\r", i, wTmp[0], xValue, ySdev, xSdev
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statements should be removed or placed behind a debug flag. These printf statements will clutter the history window in production use, which is not typical for production code in this codebase.

Suggested change
WAVE wTmp = output[i]
printf "Bin: %d Avg result: %f, xValue: %f, ySdev: %f, xSdev: %f\r", i, wTmp[0], xValue, ySdev, xSdev
WAVE wTmp = output[i]
#ifdef SWEEPFORMULA_DEBUG
printf "Bin: %d Avg result: %f, xValue: %f, ySdev: %f, xSdev: %f\r", i, wTmp[0], xValue, ySdev, xSdev
#endif

Copilot uses AI. Check for mistakes.
Comment on lines +700 to +793
printf "avg in bins mode:\r"

[s] = GetTraceColorForAverage()
Make/FREE/W/U traceColor = {s.red, s.green, s.blue}

numGroups = DimSize(input, ROWS)
binStart = binRange[0]
binEnd = binRange[1]
numBins = ceil((binEnd - binStart) / binWidth)
SFH_ASSERT(numBins < 1E6, "Maximum number of bins is 1E6.")

// Gather
Make/FREE/WAVE/N=(numBins, numGroups) binnedPerGroup
for(i = 0; i < numGroups; i += 1)
WAVE/WAVE dataSets = input[i]
WAVE/WAVE binDataSets = binData[i]
numDataSets = DimSize(dataSets, ROWS)
SFH_ASSERT(numDataSets == DimSize(binDataSets, ROWS), "The number of datasets of the input and bins are not the same for group " + num2istr(i))
for(j = 0; j < numDataSets; j += 1)
if(!WaveExists(dataSets[j]))
continue
endif
SFH_ASSERT(WaveExists(binDataSets[j]), "A bin dataset is null")
SFH_ASSERT(IsNumericWave(binDataSets[j]), "A bin dataset must be numeric")
SFH_ASSERT(DimSize(binDataSets[j], ROWS) == 1, "A bin dataset must have exactly one value")
binValue = WaveRef(binDataSets, row = j)[0]
if(binValue < binStart || binValue >= binEnd)
continue
endif
binPos = floor((binValue - binStart) / binWidth)
// Add to bin
WAVE/Z/WAVE wavesInBin = binnedPerGroup[binPos][i]
if(!WaveExists(wavesInBin))
Make/FREE/WAVE wavesInBin = {dataSets[j]}
SetNumberInWaveNote(wavesInBin, NOTE_INDEX, 1)
binnedPerGroup[binPos][i] = wavesInBin
continue
endif
idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX)
EnsureLargeEnoughWave(wavesInBin, indexShouldExist = idx)
wavesInBin[idx] = dataSets[j]
SetNumberInWaveNote(wavesInBin, NOTE_INDEX, idx + 1)
endfor
endfor
printf "Averaging bins:\r"
// avg bins with multiple filling
for(i = 0; i < numBins; i += 1)
for(j = 0; j < numGroups; j += 1)
if(!WaveExists(binnedPerGroup[i][j]))
continue
endif
WAVE/WAVE wavesInBin = binnedPerGroup[i][j]
idx = GetNumberFromWaveNote(wavesInBin, NOTE_INDEX)
Redimension/N=(idx) wavesInBin
printf "Bin %d, Group %d, NumWaves %d, ", i, j, idx
if(idx == 1)
WAVE wTmp = wavesInBin[0]
print "Result:", wTmp[0]

continue
endif
WAVE/WAVE avg = MIES_fWaveAverage(wavesInBin, 1, IGOR_TYPE_64BIT_FLOAT)
Redimension/N=(1) wavesInBin
wavesInBin[0] = avg[0]

WAVE wTmp = avg[0]
print "Result:", wTmp[0]

endfor
endfor
// avg same bins
WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numBins)
for(i = 0; i < numBins; i += 1)
Make/FREE/WAVE/N=(numGroups) sameBin
idx = 0
for(j = 0; j < numGroups; j += 1)
if(!WaveExists(binnedPerGroup[i][j]))
continue
endif
WAVE/WAVE wavesInBin = binnedPerGroup[i][j]
sameBin[idx] = wavesInBin[0]
idx += 1
endfor
printf "Bin %d, NumWaves/TotalGroups %d/%d:\r", i, idx, numGroups
if(idx == 0)
Make/FREE/D tmp = {NaN}
output[i] = tmp
else
Redimension/N=(idx) sameBin
WAVE/WAVE avg = MIES_fWaveAverage(sameBin, 1, IGOR_TYPE_64BIT_FLOAT)
output[i] = avg[0]

WAVE wTmp = output[i]
printf "Avg result: %f\r", wTmp[0]
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print statements should be removed or placed behind a debug flag. These printf statements will clutter the history window in production use.

Copilot uses AI. Check for mistakes.
Comment on lines +3196 to +3226
if(FitError == 0)
msg += "Fit Ok.\r"
endif
if((FitError & FIT_ERROR_ANY) == FIT_ERROR_ANY)
msg += "Fit Error.\r"
endif
if((FitError & FIT_ERROR_SINGULARMATRIX) == FIT_ERROR_SINGULARMATRIX)
msg += "Singular Matrix.\r"
endif
if((FitError & FIT_ERROR_OUTOFMEMORY) == FIT_ERROR_OUTOFMEMORY)
msg += "Out of memory.\r"
endif
if((FitError & FIT_ERROR_RETURNEDNANORINF) == FIT_ERROR_RETURNEDNANORINF)
msg += "Fit function returned NaN or Inf.\r"
endif
if((FitError & FIT_ERROR_FUNCREQUESTEDSTOP) == FIT_ERROR_FUNCREQUESTEDSTOP)
msg += "Function requested stop.\r"
endif
if((FitError & FIT_ERROR_REENTRANT_FIT) == FIT_ERROR_REENTRANT_FIT)
msg += "Reentrant fit function call encountered.\r"
endif
if((FitQuitReason & FIT_QUITREASON_ITERATIONLIMITREACHED) == FIT_QUITREASON_ITERATIONLIMITREACHED)
msg += "Iteration limit reached.\r"
endif
if((FitQuitReason & FIT_QUITREASON_STOPPEDBYUSER) == FIT_QUITREASON_STOPPEDBYUSER)
msg += "User stopped fit.\r"
endif
if((FitQuitReason & FIT_QUITREASON_NOCHISQUAREDECREASE) == FIT_QUITREASON_NOCHISQUAREDECREASE)
msg += "chiSquare did not decrease in last 9 iterations.\r"
endif
msg += "Iterations run: " + num2istr(fitNumIters)
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter name casing inconsistency: the function parameter is fitError (lowercase) but the code references FitError (capitalized). In Igor Pro, variables are case-insensitive, but this inconsistency reduces code readability and violates the apparent naming convention where parameters use camelCase.

Copilot uses AI. Check for mistakes.
numExp = DimSize(uniqueFiles, ROWS)
SFH_ASSERT(numExp > 0, "ivscc_apfrequency: data from at least one experiment has to be loaded")

formula = "sel = select(selsweeps(), selstimset(\"*LP_Rheo*\", \"*supra*\"), selvis(all), selivsccsweepqc(passed))\r"
Copy link

Copilot AI Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stimulus set pattern "LP_Rheo" is hardcoded into the ivscc_apfrequency operation. This reduces flexibility as it couples the operation to a specific naming convention for stimulus sets. Consider making this configurable through an optional parameter to allow users to specify their own stimulus set patterns.

Copilot uses AI. Check for mistakes.
@MichaelHuth MichaelHuth force-pushed the feature/2599-ivscc_apfrequency_operation branch from 95edd75 to 13a200c Compare February 25, 2026 13:54
@MichaelHuth MichaelHuth assigned t-b and unassigned MichaelHuth Feb 25, 2026
@MichaelHuth
Copy link
Collaborator Author

  • after JWs comment regarding CurveFit syntax, I am not sure if something for the "poly x" (and related) types need to be changed regarding extra arguments. Needs to be checked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce plotter feature to add errorbars to traces Dedicated SF operation ivscc_apfrequency

4 participants