diff --git a/API/enums/actions.m b/API/enums/backgroundActions.m similarity index 92% rename from API/enums/actions.m rename to API/enums/backgroundActions.m index 8b94a9262..e86d70234 100644 --- a/API/enums/actions.m +++ b/API/enums/backgroundActions.m @@ -1,4 +1,4 @@ -classdef actions < customEnum +classdef backgroundActions < customEnum % Ways in which a background can be included in a contrast. methods (Static) function s = toStruct() diff --git a/API/enums/coderEnums.m b/API/enums/coderEnums.m index bffc131e2..795870f6c 100644 --- a/API/enums/coderEnums.m +++ b/API/enums/coderEnums.m @@ -1,8 +1,8 @@ classdef coderEnums % An Enum class which lets MATLAB Coder use Enums without redefinition or hardcoding. properties (Constant) - actions = actions.toStruct() allowedTypes = allowedTypes.toStruct() + backgroundActions = backgroundActions.toStruct() boundHandlingOptions = boundHandlingOptions.toStruct() calculationTypes = calculationTypes.toStruct() errorCodes = errorCodes.toStruct() diff --git a/API/projectClass/baseContrasts.m b/API/projectClass/baseContrasts.m index 98c988b3d..3e5cca7c9 100644 --- a/API/projectClass/baseContrasts.m +++ b/API/projectClass/baseContrasts.m @@ -1,8 +1,18 @@ -classdef (Abstract) baseContrasts < handle +classdef (Abstract) baseContrasts < handle + % This class is the base class for ``contrastsClass`` and ``domainContrastsClass``. It holds the common + % methods used by both to add, remove and update contrasts. This class is expected to be called via inheritance and + % not directly. + % + % Parameters + % ---------- + % isDomains : logical, default: false + % Indicates if the contrast object is for a domains project. + % + % Attributes + % ---------- + % contrasts : cell + % A cell containing an the contrast entries. - % This class holds the common routines for the subclasses - % "contrastsClass.m" and "domainContrastsClass.m" - properties contrasts = {} end @@ -12,7 +22,7 @@ end properties (SetAccess = immutable) - domainsCalc + isDomains end properties (Dependent, SetAccess = private) @@ -30,22 +40,16 @@ methods (Abstract) getDisplayNames parseContrastInput - setDefaultValues end methods - function obj = baseContrasts(domainsCalc) - % Class Constructor - % The (optional) input is a logical flag to state whether - % or not this is a domains calculation. - % - % contrasts = contrastsClass() + function obj = baseContrasts(isDomains) arguments - domainsCalc {mustBeA(domainsCalc,'logical')} = false + isDomains {mustBeA(isDomains,'logical')} = false end - obj.domainsCalc = domainsCalc; + obj.isDomains = isDomains; obj.contrastAutoNameCounter = 1; end @@ -58,42 +62,102 @@ end function names = getNames(obj) - % Get a string array of the names of each of the objects - % defined in the class. - % - % contrasts.getNames() + % Returns a N x 1 cell array of names of the contrasts in the object. + % + % Returns + % ------- + % names : cell + % A cell array which contains the names of contrast entries. nContrasts = obj.numberOfContrasts; - names = strings(nContrasts, 1); + names = cell(nContrasts, 1); for i = 1:nContrasts - names(i) = obj.contrasts{i}.name; + names{i} = obj.contrasts{i}.name; + end + end + + function contrast = setDefaultValues(~, contrast) + % Sets default values to empty fields when adding a contrast. + % + % Parameters + % ---------- + % contrast: struct + % A struct containing properties of the contrast. + % + % Returns + % ------- + % contrast : struct + % A struct containing properties of the contrast with empty fields set to a default. + if ~isempty(contrast.model) + contrast.model = cellstr(contrast.model); end end - function obj = addContrast(obj, allowedNames, varargin) - % Add a contrast to the class - % A class can be added with no input parameters, just a class - % name, or a set of key-value pairs. + function obj = addContrast(obj, allowedNames, options) + % Add a new contrast to the class % - % contrasts.addContrast() - % contrasts.addContrast('New Contrast') - % contrasts.addContrast('name', 'new contrast', ... - % 'background', 'Background H2O') - if isempty(varargin) + % Examples + % -------- + % To add a new contrast with name only. + % + % >>> allowedNames = project.getAllAllowedNames(); + % >>> contrasts.addContrast(allowedNames, name='new contrast'); + % + % To add a new contrast with other properties. + % + % >>> contrasts.addContrast(allowedNames, name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', model={'Oxide', 'Bilayer tails'}); + % + % Domain ratio can be added only for domains project. + % + % >>> contrasts.addContrast(allowedNames, name='new contrast', domainRatio='Domain Ratio 1', model={'Domains 1', 'Domains 2'}); + % + % Parameters + % ---------- + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * domainRatio (char array or string, default: '') the name of the domain ratio parameter. + % * model (cell) if this is a standard layers model, this should be a list of layer names that make up the slab model for this contrast. + % For a domains standard layers model, this should be a list with exactly two domain contrast names for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + allowedNames + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.domainRatio + options.model + end + + if isempty(fields(options)) % No input at all contrastName = sprintf('New contrast %d', obj.contrastAutoNameCounter); - inputVals = {'name', contrastName}; - - elseif isscalar(varargin) - % Just name of contrast - thisName = varargin{1}; - inputVals = {'name', thisName}; - + inputVals = {'name', contrastName}; else % Everything else - inputVals = varargin; + inputVals = namedargs2cell(options); end - thisContrast = parseContrastInput(obj, allowedNames, inputVals); + thisContrast = obj.parseContrastInput(allowedNames, inputVals); thisContrast = obj.setDefaultValues(thisContrast); obj.contrasts{end+1} = thisContrast; @@ -102,20 +166,25 @@ end function obj = removeContrast(obj, row) - % Removes a contrast from the list. - % The contrast can be specified either by name or by index, but - % only one contrast can be removed at a time. + % Removes a given contrast from the project. % - % contrasts.removeContrast('Named Contrast') - % contrasts.removeContrast(1) - - % First determine if contrast is being referenced by name or - % number... - - % If the input is a string, find the index of the relevant - % contrast... + % Examples + % -------- + % To remove the second contrast in the table (contrast in row 2). + % + % >>> contrasts.removeContrast(2); + % + % To remove contrast with a specific name. + % + % >>> contrasts.removeContrast('contrast 1'); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to remove. If it is text, + % it is the name of the contrast to remove. if isText(row) - contrastNames = getAllContrastNames(obj); + contrastNames = obj.getNames(); row = find(strcmpi(contrastNames,row)); % Throw an error if the name is not matched @@ -134,45 +203,75 @@ end - function obj = setContrastModel(obj, row, allowedNames, model) - % Set the value of the model parameter in a contrast. - % The expected input is the contrast (specified either by name - % or index), the model type, the allowed values (either layers - % for standard layers or custom files for custom models) and - % either a string or cell array for the model itself. - % Note that the model can only be set here, and not in - % "addContrast" or "setContrast". + function obj = setContrast(obj, row, allowedNames, options) + % General purpose method for updating properties of an existing contrast. % - % contrasts.setContrastModel(1, allowedNames, 'Oxide Model') - obj.setContrast(row, allowedNames, 'model', model); - - end - - function obj = setContrast(obj, row, allowedNames, varargin) - % Set a value within a contrast. - % The expected input is the contrast (specified either by name - % or index), the allowed values for all parameters and a - % set of key-value pairs for the parameter values to be - % changed. + % Examples + % -------- + % To change the properties of the second contrast in the object. + % + % >>> contrasts.setContrast(2, allowedNames, name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', model='Oxide Model'}); % - % contrasts.setContrast(1, allowedNames, ... - % 'name', 'New contrast name', ... - % 'background', 'New Background') + % To change the properties of a contrast called 'Contrast 1'. + % + % >>> contrasts.setContrast('Contrast 1', allowedNames, name='new contrast', domainRatio='Domain Ratio 1', model={'Domains 1', 'Domains 2'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to update. If it is text, + % it is the name of the contrast to update. + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * domainRatio (char array or string, default: '') the name of the domain ratio parameter. + % * model (char array or string or cell string) if this is a normal standard layers model, this should be a list of layer names that make up the slab model for this contrast. + % For a domains standard layers model, this should be a list with exactly two domain contrast names for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + row + allowedNames + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.domainRatio + options.model + end % Find if we are referencing an existing contrast - if isnumeric(row) + if isnumeric(row) && all(mod(row, 1) == 0) if (row < 1 || row > obj.numberOfContrasts) - throw(exceptions.indexOutOfRange(sprintf('Contrast number %d is out of range 1 - %d', row, obj.numberOfContrasts))); + throw(exceptions.indexOutOfRange(sprintf('Contrast index %d is out of range 1 - %d', row, obj.numberOfContrasts))); end contrastIndex = row; - elseif isText(row) - present = strcmpi(row, obj.getAllContrastNames()); + present = strcmpi(row, obj.getNames()); if ~any(present) throw(exceptions.nameNotRecognised(sprintf('Contrast %s is not recognised',row))); end contrastIndex = find(present, 1); - + else + throw(exceptions.invalidType('Row should be a text or whole number.')); end thisContrast = obj.contrasts{contrastIndex}; @@ -180,7 +279,7 @@ % Check to see if the inputs are valid % Raise a warning if we try to set the model as this should be % done elsewhere - inputBlock = parseContrastInput(obj, allowedNames, varargin); + inputBlock = parseContrastInput(obj, allowedNames, namedargs2cell(options)); if isfield(inputBlock, 'name') && ~isempty(inputBlock.name) thisContrast.name = inputBlock.name; @@ -195,8 +294,8 @@ end if isfield(inputBlock, 'backgroundAction') && ~isempty(inputBlock.backgroundAction) - thisContrast.backgroundAction = validateOption(inputBlock.backgroundAction, 'actions',... - sprintf('backgroundAction must be a actions enum or one of the following strings (%s)', strjoin(actions.values(), ', '))).value; + thisContrast.backgroundAction = validateOption(inputBlock.backgroundAction, 'backgroundActions',... + sprintf('backgroundAction must be a actions enum or one of the following strings (%s)', strjoin(backgroundActions.values(), ', '))).value; end if isfield(inputBlock, 'bulkIn') && ~isempty(inputBlock.bulkIn) @@ -235,26 +334,13 @@ end - function contrastNames = getAllContrastNames(obj) - % Get the names of all contrasts defined in the class. - % - % contrasts.getAllContrastNames() - nContrasts = obj.numberOfContrasts; - contrastNames = cell(1,nContrasts); - - for i = 1:nContrasts - thisContrast = obj.contrasts{i}; - contrastNames{i} = thisContrast.name; - end - end - function contrastStruct = toStruct(obj) - % Convert the contrasts class to a struct. - % This routine deals with properties common to all contrast - % classes. The expected input is the allowed names for each - % parameter, the model type and the data table from the data class. - % - % contrasts.toStruct() + % Converts the base contrasts class to a struct. + % + % Returns + % ------- + % contrastStruct : struct + % A struct which contains the properties for all the contrast entries. nContrasts = obj.numberOfContrasts; contrastNames = cell(1,nContrasts); @@ -268,11 +354,7 @@ end function displayContrastsObject(obj) - % Display the contrasts object as a table. - % The subclass routine needs to pass in the rowNames for its - % particular properties. - % - % contrasts.displayContrastsObject() + % Prints the contrasts object to the console in a tabular format. rowNames = obj.displayNames; nContrasts = obj.numberOfContrasts; maxModelSize = 1; @@ -347,5 +429,3 @@ function displayContrastsObject(obj) end end - - diff --git a/API/projectClass/contrastsClass.m b/API/projectClass/contrastsClass.m index 99137cc0a..abb6597c9 100644 --- a/API/projectClass/contrastsClass.m +++ b/API/projectClass/contrastsClass.m @@ -1,26 +1,33 @@ classdef contrastsClass < baseContrasts - - % This class holds the parameters for each contrast used in the - % simulation - + % This class holds the parameters for each contrast used in the simulation + % + % Examples + % -------- + % >>> contrasts = contrastsClass(); + % >>> contrasts = contrastsClass(true); + % + % Parameters + % ---------- + % isDomains : logical, default: false + % Indicates if the contrast object is for a domains project. + % + % Attributes + % ---------- + % contrasts : cell + % A cell containing all the contrast entries. methods - function obj = contrastsClass(calcType) - % Class Constructor - % The (optional) inputs is a logical flag to state whether - % or not this is a domains calculation. - % - % contrasts = contrastsClass() + function obj = contrastsClass(isDomains) arguments - calcType.domains {mustBeA(calcType.domains,'logical')} = false + isDomains {mustBeA(isDomains,'logical')} = false end - obj@baseContrasts(calcType.domains) + obj@baseContrasts(isDomains) end function names = getDisplayNames(obj) names = ["Name"; "Data"; "Background"; "Background Action"; "Bulk in"; "Bulk out"; "Scalefactor"; "Resolution"; "Resample"; "Repeat Layers"; "Model"]; - if obj.domainsCalc + if obj.isDomains names = [names(1:end-1); "Domain Ratio"; names(end)]; end end @@ -33,7 +40,11 @@ % field of the contrasts and if it matches nameChange.oldName % then this is updated to nameChange.newName % - % contrasts.updateDataName(nameChange) + % Parameters + % ---------- + % nameChange: struct + % A struct which contains the former name ``oldName`` and the new name ``newName`` of the dataset or + % an empty array if name is not changed oldName = nameChange.oldName; newName = nameChange.newName; @@ -47,13 +58,21 @@ end function contrastStruct = toStruct(obj, allowedNames, modelType, dataTable) - % Convert the contrasts class to a struct. - % This routine builds on that in the base class by dealing with - % the additional properties defined in this subclass. - % The expected input is the allowed names for each parameter, - % the model type and the data table from the data class. - % - % contrasts.toStruct(allowedNames, 'standard layers', dataTable) + % Converts the contrasts class to a struct. + % + % Parameters + % ---------- + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % modelType: modelTypes + % The layer model type which can be 'standard layers', 'custom layers', or 'custom xy'. + % dataTable: table + % A table from the dataClass. + % + % Returns + % ------- + % contrastStruct : struct + % A struct which contains the properties for all the contrast entries. % Call superclass version for common properties contrastStruct = toStruct@baseContrasts(obj); @@ -86,43 +105,39 @@ case modelTypes.StandardLayers.value thisModel = thisContrast.model; thisArray = ones(1, length(thisModel)); - if obj.domainsCalc + if obj.isDomains for n = 1:length(thisModel) - thisLayerNum = find(strcmpi(thisModel{n}, allowedNames.domainContrastNames)); - thisArray(n) = thisLayerNum; + thisArray(n) = obj.findParameterIndex(thisModel{n}, allowedNames.domainContrastNames, "domain contrast"); end else for n = 1:length(thisModel) - thisLayerNum = find(strcmpi(thisModel{n}, allowedNames.layerNames)); - thisArray(n) = thisLayerNum; + thisArray(n) = obj.findParameterIndex(thisModel{n}, allowedNames.layerNames, "layer"); end end contrastLayers{i} = thisArray; contrastCustomFile(i) = NaN; otherwise contrastLayers{i} = []; - whichFile = thisContrast.model; - thisContrastFileNum = find(strcmpi(whichFile, allowedNames.customFileNames)); - contrastCustomFile(i) = thisContrastFileNum; + contrastCustomFile(i) = obj.findParameterIndex(thisContrast.model{1}, allowedNames.customFileNames, "custom file"); end if isfield(thisContrast, 'domainRatio') - contrastDomainRatios(i) = find(strcmpi(thisContrast.domainRatio,allowedNames.domainRatioNames)); + contrastDomainRatios(i) = obj.findParameterIndex(thisContrast.domainRatio, allowedNames.domainRatioNames, "domain ratio"); else contrastDomainRatios(i) = -1; end - - contrastBackgrounds(i) = find(strcmpi(thisContrast.background,allowedNames.backgroundNames)); + + contrastBackgrounds(i) = obj.findParameterIndex(thisContrast.background, allowedNames.backgroundNames, "background"); contrastBackgroundActions{i} = thisContrast.backgroundAction; - contrastBulkIns(i) = find(strcmpi(thisContrast.bulkIn,allowedNames.bulkInNames)); - contrastBulkOuts(i) = find(strcmpi(thisContrast.bulkOut,allowedNames.bulkOutNames)); - contrastScalefactors(i) = find(strcmpi(thisContrast.scalefactor,allowedNames.scalefactorNames)); - contrastResolutions(i) = find(strcmpi(thisContrast.resolution,allowedNames.resolutionNames)); + contrastBulkIns(i) = obj.findParameterIndex(thisContrast.bulkIn, allowedNames.bulkInNames, "bulk in"); + contrastBulkOuts(i) = obj.findParameterIndex(thisContrast.bulkOut, allowedNames.bulkOutNames, "bulk out"); + contrastScalefactors(i) = obj.findParameterIndex(thisContrast.scalefactor, allowedNames.scalefactorNames, "scalefactor"); + contrastResolutions(i) = obj.findParameterIndex(thisContrast.resolution, allowedNames.resolutionNames, "resolution"); resample(i) = thisContrast.resample; contrastRepeatLayers(i) = thisContrast.repeatLayers; - - thisDataVal = find(strcmpi(thisContrast.data,allowedNames.dataNames)); - if ~isempty(thisDataVal) + + if ~isempty(thisContrast.data) + thisDataVal = obj.findParameterIndex(thisContrast.data, allowedNames.dataNames, "dataset"); actualData = dataTable{thisDataVal,2}{:}; if ~isempty(actualData) dataPresent(i) = 1; @@ -135,9 +150,9 @@ simulationLimits{i} = dataTable{thisDataVal,4}{:}; contrastData{i} = dataTable{thisDataVal,2}{:}; else - dataLimits{i} = [0 0]; - simulationLimits{i} = [0 0]; - contrastData{i} = [0 0 0]; + dataLimits{i} = [0, 0]; + simulationLimits{i} = dataClass.defaultSimRange; + contrastData{i} = [0, 0, 0]; end end @@ -160,14 +175,22 @@ end function inputBlock = parseContrastInput(obj, allowedNames, inputValues) - % Parse the parameters given for the contrast, assigning + % Parses the given keyword/value pairs of properties for the contrast, assigning % default values to those unspecified and ensuring specified % values are of the correct type, and included in the list of % allowed names where necessary. - % - % contrastsClass.parseContrastInput(allowedNames, ... - % 'name', 'Contrast Name', ... - % 'background', 'Background H2O') + % + % Parameters + % ---------- + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % inputValues: cell + % A cell containing keyword/value pairs of properties for the contrast. + % + % Returns + % ------- + % inputBlock : struct + % A struct containing the given properties of the contrast. defaultName = ''; defaultBackground = ''; defaultBackgroundAction = ''; @@ -201,7 +224,7 @@ addParameter(p,'resample', defaultResample, @islogical); addParameter(p,'repeatLayers', defaultRepeatLayers, @isnumeric); - if obj.domainsCalc + if obj.isDomains defaultDomainRatio = ''; expectedDomainRatio = cellstr(allowedNames.domainRatioNames); addParameter(p,'domainRatio', defaultDomainRatio, @isText); @@ -211,36 +234,116 @@ parse(p, inputValues{:}); inputBlock = p.Results; - inputBlock.data = obj.validateExactString(inputBlock.data, expectedData); - inputBlock.background = obj.validateExactString(inputBlock.background, expectedBackground); - inputBlock.bulkIn = obj.validateExactString(inputBlock.bulkIn, expectedBulkIn); - inputBlock.bulkOut = obj.validateExactString(inputBlock.bulkOut, expectedBulkOut); - inputBlock.scalefactor = obj.validateExactString(inputBlock.scalefactor, expectedScalefactor); - inputBlock.resolution = obj.validateExactString(inputBlock.resolution, expectedResolution); + inputBlock.data = obj.validateExactString(inputBlock.data, expectedData, "dataset"); + inputBlock.background = obj.validateExactString(inputBlock.background, expectedBackground, "background"); + inputBlock.bulkIn = obj.validateExactString(inputBlock.bulkIn, expectedBulkIn, "bulk in"); + inputBlock.bulkOut = obj.validateExactString(inputBlock.bulkOut, expectedBulkOut, "bulk out"); + inputBlock.scalefactor = obj.validateExactString(inputBlock.scalefactor, expectedScalefactor, "scalefactor"); + inputBlock.resolution = obj.validateExactString(inputBlock.resolution, expectedResolution, "resolution"); inputBlock.repeatLayers = obj.validatePositiveInteger(inputBlock.repeatLayers); if ~isfield(allowedNames, 'layerNames') && any(inputBlock.repeatLayers ~= 1) warning("Repeat Layers are only supported for standard layers calculations") end - + inputBlock.model = obj.validateContrastModel(inputBlock.model, allowedNames); - if obj.domainsCalc - inputBlock.domainRatio = obj.validateExactString(inputBlock.domainRatio, expectedDomainRatio); + if obj.isDomains + inputBlock.domainRatio = obj.validateExactString(inputBlock.domainRatio, expectedDomainRatio, "domain ratio"); end end + + function contrast = setDefaultValues(obj, contrast) + % Sets default values to empty fields when adding a contrast. + % + % Parameters + % ---------- + % contrast: struct + % A struct containing properties of the contrast. + % + % Returns + % ------- + % contrast : struct + % A struct containing properties of the contrast with empty fields set to default. + contrast = setDefaultValues@baseContrasts(obj, contrast); + + if isempty(contrast.backgroundAction) + contrast.backgroundAction = backgroundActions.Add.value; + else + contrast.backgroundAction = validateOption(contrast.backgroundAction, 'backgroundActions',... + sprintf('backgroundAction must be a actions enum or one of the following strings (%s)', strjoin(backgroundActions.values(), ', '))).value; + end + if isempty(contrast.resample) + contrast.resample = false; + end + + if isempty(contrast.repeatLayers) + contrast.repeatLayers = 1; + end + + end end methods(Access = private) + function output = findParameterIndex(~, input, allowedNames, inputDesc) + % Get the index of an input name in list of allowed names. + % + % Parameters + % ---------- + % input: string or char array + % A name to validate. + % allowedNames: cell + % A cell containing the valid names. + % inputDesc: string or char array + % A description of the input. + % + % Returns + % ------- + % output: whole number + % Index of the input name in the allowed list of names. + if isempty(input) + throw(exceptions.invalidValue(sprintf("All contrasts must have a valid %s.", inputDesc))); + end + found = strcmpi(input, allowedNames); + if ~any(found) + if isempty(allowedNames) + msg = sprintf('"%s" is not a recognised %s. Did you forget to add the %s?', input, inputDesc, inputDesc); + else + msg = sprintf('"%s" is not a recognised %s. The allowed names are: "%s".', input, inputDesc, strjoin(allowedNames, '", "')); + end + throw(exceptions.nameNotRecognised(msg)); + end + output = find(found, 1); + end - function output = validateExactString(~, input, allowedNames) + function output = validateExactString(~, input, allowedNames, inputDesc) + % Validates input name is found in list of allowed names. + % + % Parameters + % ---------- + % input: string or char array + % A name to validate. + % allowedNames: cell + % A cell containing the valid names. + % inputDesc: string or char array + % A description of the input. + % + % Returns + % ------- + % output: string or char array + % The validated name. if isempty(input) output = ''; return end found = strcmpi(input, allowedNames); if ~any(found) - throw(exceptions.nameNotRecognised(sprintf('The input "%s" is not recognised. The allowed names are: "%s".', input, strjoin(allowedNames, '", "')))); + if isempty(allowedNames) + msg = sprintf('The input "%s" is not a recognised %s. Did you forget to add the %s?', input, inputDesc, inputDesc); + else + msg = sprintf('The input "%s" is not a recognised %s. The allowed names are: "%s".', input, inputDesc, strjoin(allowedNames, '", "')); + end + throw(exceptions.nameNotRecognised(msg)); end output = allowedNames{find(found, 1)}; end @@ -257,6 +360,19 @@ end function model = validateContrastModel(obj, input, allowedNames) + % Validates contrast model. + % + % Parameters + % ---------- + % input: string or char array or cell string + % The name(s) in the contrast model. + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % + % Returns + % ------- + % model: cell string + % The validated name(s) in the contrast model. if isempty(input) model = ''; return @@ -282,7 +398,7 @@ throw(exceptions.invalidValue('At least one layer must be added to the contrast model in a standard layers project. Did you forget to add the layers?')); end - if obj.domainsCalc && length(inputArray) ~= 2 + if obj.isDomains && length(inputArray) ~= 2 throw(exceptions.invalidValue('Exactly two model values are required for ''standard layers'' with domains')); end end @@ -301,31 +417,4 @@ end - methods(Static) - - function contrast = setDefaultValues(contrast) - % Set non-empty default values when adding a contrast. - if ~isempty(contrast.model) - contrast.model = cellstr(contrast.model); - end - - if isempty(contrast.backgroundAction) - contrast.backgroundAction = actions.Add.value; - else - contrast.backgroundAction = validateOption(contrast.backgroundAction, 'actions',... - sprintf('backgroundAction must be a actions enum or one of the following strings (%s)', strjoin(actions.values(), ', '))).value; - end - - if isempty(contrast.resample) - contrast.resample = false; - end - - if isempty(contrast.repeatLayers) - contrast.repeatLayers = 1; - end - - end - - end - end diff --git a/API/projectClass/dataClass.m b/API/projectClass/dataClass.m index 9d7c9bc21..2e6996f7d 100644 --- a/API/projectClass/dataClass.m +++ b/API/projectClass/dataClass.m @@ -29,7 +29,7 @@ % varTable : table % A table object that contains the data entries. - properties (Constant, Access = private) + properties (Constant, Hidden) defaultSimRange = [0.005, 0.7] end diff --git a/API/projectClass/domainContrastsClass.m b/API/projectClass/domainContrastsClass.m index f106bb24b..fa157a104 100644 --- a/API/projectClass/domainContrastsClass.m +++ b/API/projectClass/domainContrastsClass.m @@ -1,24 +1,25 @@ classdef domainContrastsClass < baseContrasts - % A simplified version of the contrast class that allows specification % of the model only (i.e. with no data). This is used for domains % calculations. methods - function names = getDisplayNames(~) names = ["Name"; "Model"]; end function contrastStruct = toStruct(obj, allowedNames, ~, ~) - % Convert the contrasts class to a struct. - % This routine builds on that in the base class by dealing with - % the additional properties defined in this subclass. - % The expected input is the allowed names for each parameter. - % - % domainContrasts.toStruct(allowedNames, ~, ~) - - % Call superclass version for common properties + % Converts the domains contrasts class to a struct. + % + % Parameters + % ---------- + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % + % Returns + % ------- + % contrastStruct : struct + % A struct which contains the properties for all the contrast entries. contrastStruct = toStruct@baseContrasts(obj); nContrasts = obj.numberOfContrasts; @@ -44,7 +45,17 @@ % values are of the correct type, and included in the list of % allowed names where necessary. % - % contrastsClass.parseContrastInput(~, allowedNames, 'name', 'Contrast Name') + % Parameters + % ---------- + % allowedNames: struct + % A struct containing the valid names that can be referenced in the contrast. + % inputValues: cell + % A cell containing keyword/value pairs of properties for the contrast. + % + % Returns + % ------- + % inputBlock : struct + % A struct containing properties of the contrast with empty fields set to default. defaultName = ''; defaultModel = ''; @@ -67,6 +78,19 @@ methods(Access = private) function model = validateDomainContrastModel(~, input, allowedModelNames) + % Validates domain contrast model. + % + % Parameters + % ---------- + % input: string or char array or cell string + % The name(s) in the domain contrast model. + % allowedNames: struct + % A struct containing the valid names that can be referenced in the domain contrast. + % + % Returns + % ------- + % model: cell string + % The validated name(s) in the domain contrast model. if isempty(input) model = ''; return @@ -86,15 +110,4 @@ end - methods(Static) - - function contrast = setDefaultValues(contrast) - % Set non-empty default values when adding a contrast. - if ~isempty(contrast.model) - contrast.model = cellstr(contrast.model); - end - end - - end - end diff --git a/API/projectClass/domainsClass.m b/API/projectClass/domainsClass.m index 23510c2de..a629ecbd5 100644 --- a/API/projectClass/domainsClass.m +++ b/API/projectClass/domainsClass.m @@ -1,13 +1,53 @@ classdef domainsClass < projectClass - - % Class definition for Standard Layers with no absorption. - % Layers defined in terms of thickness, roughness, real SLD and - % hydration. - % - % Sub objects used are: - % parametersClass - parameter definition with priors - % domainContrastsClass - additional contrasts object for domains - + % ``domainsClass`` stores all the information that describes a domains experiment which is essential for running a RAT domains calculation. + % The ``domainsClass`` contains similar components to a normal project such as the parameters, backgrounds, resolutions, custom files, + % data, contrast etc, but also stores the domains ratio and domains contrast which are unique to a domains calculation. The + % ``domainsClass`` provides a number of methods to add, remove, and update these components. For example, for domain ratio, the + % ``addDomainsRatio``, ``removeDomainsRatio``, and ``setDomainsRatio`` methods are available for adding, removing, and updating parameters. + % + % Examples + % -------- + % >>> domains = domainsClass(); + % >>> domains = domainsClass("Example Project"); + % >>> domains = domainsClass("Example Project", 'custom layers', 'substrate/liquid', true); + % + % Parameters + % ---------- + % experimentName : string or char array, default: '' + % The name of the domains project. + % modelType : modelTypes, default: modelTypes.StandardLayers + % The layer model type which can be 'standard layers', 'custom layers', or 'custom xy'. + % geometry : geometryOptions, default: geometryOptions.AirSubstrate + % The geometry to use which can be 'air/substrate' or 'substrate/liquid'. + % absorption : logical, default: false + % Indicates whether imaginary component is used for the SLD value in layers. + % + % Attributes + % ---------- + % parameters : parametersClass + % The project parameters. + % bulkIn : parametersClass + % The bulkIn parameters. + % bulkOut : parametersClass + % The bulkOut parameters. + % scalefactors : parametersClass + % The scalefactors parameters. + % layers : layersClass + % An object which contains the layers information. + % data : dataClass + % An object which contains the data table. + % customFile : Custom file object + % An object which contains the defined customFiles. + % background : backgroundsClass object + % An object which contains defined backgrounds and background parameters. + % resolution : resolutionClass object + % An object which contains defined resolutions and resolution parameters. + % contrasts : contrastsClass object + % An object which contains contrast information. + % domainRatio : parametersClass + % The domain ratio parameters. + % domainContrasts : domainContrastsClass + % An object which contains domain contrast information. properties domainRatio % Class for specifying the ratio between domains domainContrasts % Modified contrast class with no data for domains @@ -16,15 +56,6 @@ methods function obj = domainsClass(experimentName, modelType, geometry, absorption) - % Creates a Project object for a domains calculation. - % The input arguments are the experiment name which is a char - % array; the model type, which is a modelTypes enum; the - % geometry, which is a geometryOptions enum; and a logical to - % state whether or not absorption terms are included in the - % refractive index. - % All of the arguments are optional. - % - % project = domainsClass('New experiment'); arguments experimentName {mustBeTextScalar} = '' modelType = modelTypes.StandardLayers @@ -37,7 +68,7 @@ obj.calculationType = calculationTypes.Domains.value; % Create a contrasts class for a domains calculation - obj.contrasts = contrastsClass(domains=true); + obj.contrasts = contrastsClass(true); % For a domains calculation, initialise domain ratio parameter % class and, for a standard layers model, secondary contrasts @@ -49,19 +80,30 @@ end function projectObj = toProjectClass(obj) - % Alias of the converter routine from domainsClass to - % projectClass. - % This routine takes the currently defined project and - % converts it to a normal calculation, preserving all - % currently defined properties. + % Creates a new projectClass object and copies the properties from current domainsClass. % - % normalProject = project.toProjectClass(); + % Examples + % -------- + % >>> normalProject = domains.toProjectClass(); + % + % Returns + % ------- + % projectObj : projectClass + % An instance of projectClass with the common properties copied from the domainsClass. projectObj = obj.projectClass(); end function names = getAllAllowedNames(obj) - % Returns a cell array of all currently - % set parameter names for the project. + % Returns a struct with all currently set names of normal parameters, background and resolution + % parameters, backgrounds, resolutions, bulk-ins, bulk-outs, scalefactors, data, custom files, + % contrast model, domain ratios, and domain contrasts for the project. + % + % Returns + % ------- + % names : struct + % A struct with names of all the normal parameters, background and resolution + % parameters, backgrounds, resolutions, bulk-ins, bulk-outs, scalefactors, data, custom files, + % contrast model, domain ratios, and domain contrasts entries in the project. names = getAllAllowedNames@projectClass(obj); names.domainRatioNames = obj.domainRatio.getNames(); if isa(obj.domainContrasts, 'domainContrastsClass') @@ -71,73 +113,320 @@ end % ---------------------------------------------------------------- - % - % Editing of Domains Contrasts Block + % Editing of Contrasts Block + + function obj = addContrast(obj, options) + % Add a new contrast to the project. + % + % Examples + % -------- + % To add a new contrast with name only. + % + % >>> domains.addContrast(name='new contrast'); + % + % To add a new contrast with other properties. + % + % >>> domains.addContrast(name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', ... + % domainRatio='Domain Ratio 1', model={'Domains 1', 'Domains 2'}); + % + % Parameters + % ---------- + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * domainRatio (char array or string, default: '') the name of the domain ratio parameter. + % * model (cell) if this is a standard layers model, this should be a list of domain contrast names for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.domainRatio + options.model + end + args = namedargs2cell(options); + obj.contrasts.addContrast(obj.getAllAllowedNames(), args{:}); + end + function obj = setContrast(obj, row, options) + % General purpose method for updating properties of an existing contrast. + % + % Examples + % -------- + % To change the properties of the second contrast in the object. + % + % >>> domains.setContrast(2, name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', model={'Domains 1', 'Domains 2'}}); + % + % To change the properties of a contrast called 'Contrast 1'. + % + % >>> domains.setContrast('Contrast 1', name='new contrast', domainRatio='Domain Ratio 1', model={'Domains 1', 'Domains 2'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to update. If it is text, + % it is the name of the contrast to update. + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * domainRatio (char array or string, default: '') the name of the domain ratio parameter. + % * model (char array or string or cell string) if this is a standard layers model, this should be a list of domain contrast names for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + row + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.domainRatio + options.model + end + args = namedargs2cell(options); + obj.contrasts.setContrast(row, obj.getAllAllowedNames(), args{:}); + end + function obj = setContrastModel(obj, row, model) - % Edits the model of an existing contrast parameter. Expects - % the index of contrast parameter and cell array of layer names + % Updates the model of an existing contrast. % - % project.setContrastModel(1, {'layer 1'}) - - % Make a different allowed list depending on whether - % it is custom or layers - allowedValues = obj.getAllAllowedNames(); - - % Call the setContrastModel method - obj.contrasts.setContrastModel(row, allowedValues, model); + % Examples + % -------- + % To change the model of the second contrast in the project. + % + % >>> domains.setContrastModel(2, {'Domains 1', 'Domains 2'}); + % + % To change the properties of a contrast called 'Contrast 1'. + % + % >>> domains.setContrastModel('Contrast 1', {'Domains 1', 'Domains 2'}); + % + % To change multiple contrasts at once. The snippet below will change 1, 2, and 3. + % + % >>> domains.setContrastModel(1:3, {'Domains 1', 'Domains 2'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to update. If it is text, + % it is the name of the contrast to update. + % model: char array or string or cell string + % If this is a standard layers model, this should be a list of domain contrast names for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + setContrastModel@projectClass(obj, row, model); end % ------------------------------------------------------------------- % Editing of Domain Ratio block - function obj = addDomainRatio(obj, varargin) + function obj = addDomainRatio(obj, name, min, value, max, fit, priorType, mu, sigma) % Adds a new domain ratio parameter. Expects the name % of domain ratio, min, value, max, and if fit is off or on % % project.addDomainRatio('Domain Ratio 2', 0.4, 0.5, 0.6, true); - obj.domainRatio.addParameter(varargin{:}); + % Adds an new domain ratio to the project. + % + % Examples + % -------- + % To add a new domain ratio with no properties and an autogenerated name. + % + % >>> domains.addDomainRatio(); + % + % To add a domain ratio with all available properties. + % + % >>> domains.addDomainRatio('New Domain Ratio', 0.4, 0.5, 0.6, true, 'gaussian', 1, 5); + % + % Other examples of adding domain ratios with a subset of properties. + % + % >>> domains.addDomainRatio('New Domain Ratio'); % Domain ratio name only with others set to default + % >>> domains.addDomainRatio('New Domain Ratio', 0.4); % Domain ratio name and min only. Value and max will be set to 0.4 to keep limits valid + % >>> domains.addDomainRatio('New Domain Ratio', 0.4, 0.5, 0.6, true); % priors will be default + % + % Parameters + % ---------- + % name : string or char array, default: auto-generated name + % The name of the domain ratio. + % min : double, default: 0.0 + % The minimum value that the domain ratio could take when fitted. + % value : double, default: 0.0 + % The value of the domain ratio, default will be equal to ``min`` if this is not set. + % max : double, default: 0.0 + % The maximum value that the domain ratio could take when fitted, default will be equal to ``value`` if this is not set. + % fit : logical, default: false + % Whether the domain ratio should be fitted in a calculation. + % priorType : PriorTypes, default: PriorTypes.Uniform + % For Bayesian calculations, whether the prior likelihood is assumed to be ‘uniform’, ‘gaussian’, or ‘jeffreys’. + % mu : double, default: 0.0 + % If the prior type is Gaussian, the mean of the Gaussian function for the prior likelihood. + % sigma : double, default: Inf + % If the prior type is Gaussian, the standard deviation of the Gaussian function for the prior likelihood. + arguments + obj + name {mustBeTextScalar} = '' + min {mustBeNumeric, isscalar} = 0.0 + value {mustBeScalarOrEmpty, mustBeNumeric} = [] + max {mustBeScalarOrEmpty, mustBeNumeric} = [] + fit {mustBeA(fit, 'logical')} = false + priorType = priorTypes.Uniform + mu {mustBeNumeric, isscalar} = 0.0 + sigma {mustBeNumeric, isscalar} = Inf + end + obj.domainRatio.addParameter(name, min, value, max, fit, priorType, mu, sigma); end - function obj = removeDomainRatio(obj, varargin) - % Removes specified domain ratio parameter. Expects the - % name/index of domain ratio to remove + function obj = removeDomainRatio(obj, row) + % Removes a domain ratio from the project. + % + % Examples + % -------- + % To remove the second domain ratio in the table (domain ratio in row 2). + % + % >>> domains.removeDomainRatio(2); + % + % To remove domain ratio with a specific name. + % + % >>> domains.removeDomainRatio('Domain Ratio 2'); % - % project.removeDomainRatio(2); - obj.domainRatio.removeParameter(varargin{:}); + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the domain ratio to remove. If it is text, + % it is the name of the domain ratio to remove. + obj.domainRatio.removeParameter(row); end - function obj = setDomainRatio(obj, varargin) - % Edits an existing domain ratio parameter. Expects the - % index of domain ratio to edit and key-value pairs + function obj = setDomainRatio(obj, row, options) + % General purpose method for updating properties of an existing domain ratio. + % Any unset property will remain unchanged. % - % project.setDomainRatio(1, 'name','Domain Ratio 1', 'value', 0.55); - obj.domainRatio.setParameter(varargin{:}); + % Examples + % -------- + % To change the name and value of the second domain ratio in the table (domain ratio in row 2). + % + % >>> domains.setDomainRatio(2, name='Domain Ratio 1', value=0.55); + % + % To change the all properties of a domain ratio called 'Domain Ratio 1'. + % + % >>> domains.setDomainRatio('Domain Ratio 1', name='New Domain Ratio', min=0.4, value=0.5, max=0.6, ... + % >>> fit=true, priorType=gaussian, mu=1, sigma=5); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the domain ratio to update. If it is text, + % it is the name of the domain ratio to update. + % options + % Keyword/value pair to properties to update for the specific domain ratio. + % * name (char array or string, default: '') the new name of the domain ratio. + % * min (double, default: []) the new minimum value of the domain ratio. + % * value (double, default: []) the new value of the domain ratio. + % * max (double, default: []) the new maximum value of the domain ratio. + % * fit (logical, default: logical.empty()) the new fit flag of the domain ratio. + % * priorTypes (priorTypes, default: priorTypes.empty()) the new prior type of the domain ratio. + % * mu (double, default: []) the new mean of the Gaussian function for the prior. + % * sigma (double, default: []) The new standard deviation of the Gaussian function for the prior. + arguments + obj + row + options.name {mustBeTextScalar} = '' + options.min {mustBeScalarOrEmpty, mustBeNumeric} = [] + options.value {mustBeScalarOrEmpty, mustBeNumeric} = [] + options.max {mustBeScalarOrEmpty, mustBeNumeric} = [] + options.fit {mustBeScalarOrEmptyLogical} = logical.empty() + options.priorType = priorTypes.empty() + options.mu {mustBeScalarOrEmpty, mustBeNumeric} = [] + options.sigma {mustBeScalarOrEmpty, mustBeNumeric} = [] + end + args = namedargs2cell(options); + obj.domainRatio.setParameter(row, args{:}); end % ---------------------------------------------------------------- - % - % Editing of Domains Contrasts Block + % Editing of Domains Contrasts Block - function obj = addDomainContrast(obj, varargin) - % Adds a new domainContrast parameter. Expects a parameter name, - % and with key-value pairs with one or more of the following - % "bulk in", "bulk out", "model" + function obj = addDomainContrast(obj, options) + % Add a new domain contrast to the project. + % + % Examples + % -------- + % To add a new domain contrast with name only. + % + % >>> domains.addDomainContrast(name='new domain contrast'); + % + % To add a new domain contrast with model. % - % project.addDomainContrast('domainContrast 1', 'bulkIn', 'Silicon'); + % >>> domains.addDomainContrast(name='new domain contrast', model='Oxide'); + % >>> domains.addDomainContrast(name='new domain contrast', model={'Oxide', 'Bilayer tails'}); + % + % Parameters + % ---------- + % options + % Keyword/value pair to properties to update for the specific domain contrast. + % * name (char array or string, default: '') the name of the domain contrast. + % * model (cell) this should be a list of layer names that make up the slab model for this domain contrast. + arguments + obj + options.name + options.model + end if isa(obj.domainContrasts, 'domainContrastsClass') - allowedNames = obj.getAllAllowedNames(); - obj.domainContrasts.addContrast(allowedNames, varargin{:}); + args = namedargs2cell(options); + obj.domainContrasts.addContrast(obj.getAllAllowedNames(), args{:}); else throw(exceptions.invalidProperty(sprintf('Domain Contrasts are not defined for the model type: %s', obj.modelType))); end end function obj = removeDomainContrast(obj, row) - % Removes a specified domainContrast parameter. Expects - % index or name of resolution to remove + % Removes a given domain contrast from the project. % - % project.removeDomainContrast(1); + % Examples + % -------- + % To remove the second domain contrast in the table (domain contrast in row 2). + % + % >>> domains.removeDomainContrast(2); + % + % To remove domain contrast with a specific name. + % + % >>> domains.removeDomainContrast('domain contrast 1'); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the domain contrast to remove. If it is text, + % it is the name of the domain contrast to remove. if isa(obj.domainContrasts, 'domainContrastsClass') obj.domainContrasts.removeContrast(row); else @@ -145,32 +434,68 @@ end end - function obj = setDomainContrast(obj, row, varargin) - % Allow setting of all parameters in terms of name value pairs. - % First input must be domainContrast number or name, subsequent - % inputs are name / value pairs for the parts involved + function obj = setDomainContrast(obj, row, options) + % General purpose method for updating properties of an existing domain contrast. + % + % Examples + % -------- + % To change the properties of the second domain contrast in the object. + % + % >>> domains.setDomainContrast(2, name='new domain contrast', model='Oxide Model'}); % - % project.setContrast(1, 'name', 'domainContrast') + % To change the properties of a domain contrast called 'Domain Contrast 1'. + % + % >>> domains.setDomainContrast('Domain Contrast 1', name='new domain contrast', model={'Oxide', 'Bilayer tails'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the domain contrast to update. If it is text, + % it is the name of the domain contrast to update. + % options + % Keyword/value pair to properties to update for the specific domain contrast. + % * name (char array or string, default: '') the name of the domain contrast. + % * model (char array or string or cell string) this should be a list of layer names that make up the slab model for this contrast. + arguments + obj + row + options.name + options.model + end if isa(obj.domainContrasts, 'domainContrastsClass') - % Get the list of allowed values depending on what is - % set for the other contrasts. - allowedValues = obj.getAllAllowedNames(); - - % Call the setContrast method - obj.domainContrasts.setContrast(row, allowedValues, varargin{:}); + args = namedargs2cell(options); + obj.domainContrasts.setContrast(row, obj.getAllAllowedNames(), args{:}); else throw(exceptions.invalidProperty(sprintf('Domain Contrasts are not defined for the model type: %s', obj.modelType))); end end function obj = setDomainContrastModel(obj, row, model) - % Edits the model of an existing contrast parameter. Expects - % the index of contrast parameter and cell array of layer names + % Updates the model of an existing domain contrast. % - % project.setDomainContrastModel(1, {'layer 1'}) + % Examples + % -------- + % To change the model of the second domain contrast in the project. + % + % >>> domains.setDomainContrastModel(2, 'Oxide Model'}); + % + % To change the properties of a domain contrast called 'Contrast 1'. + % + % >>> domains.setDomainContrastModel('Contrast 1', {'Domains 1', 'Domains 2'}); + % + % To change multiple domain contrasts at once. The snippet below will change 1, 2, and 3. + % + % >>> domains.setDomainContrastModel(1:3, {'Layer 1'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the domain contrast to update. If it is text, + % it is the name of the domain contrast to update. + % model: char array or string or cell string + % This should be a list of layer names that make up the slab model for this domain contrast. if isa(obj.domainContrasts, 'domainContrastsClass') - allowedValues = obj.getAllAllowedNames(); - obj.domainContrasts.setContrastModel(row, allowedValues, model); + obj.setDomainContrast(row, model=model); else throw(exceptions.invalidProperty(sprintf('Domain Contrasts are not defined for the model type: %s', obj.modelType))); end @@ -179,8 +504,12 @@ % ---------------------------------------------------------------- function outStruct = toStruct(obj) - % Converts the domains class parameters into a struct array - % for input into the RAT toolbox + % Converts the domainsClass into a structure array. + % + % Returns + % ------- + % outStruct : struct + % A struct which contains the properties from the domainsClass. mainStruct = toStruct@projectClass(obj); @@ -215,11 +544,16 @@ % ------------------------------------------------------------------ - methods (Access = protected, Hidden) + methods (Access = protected) function obj = setLayersAndContrasts(obj, oldModel) % Adjust layers and contrast objects when the model type is % changed. + % + % Parameters + % ---------- + % oldModel : modelTypes + % The model type of the project before it was changed. setLayersAndContrasts@projectClass(obj, oldModel); % Also need to define domain contrasts as necessary @@ -237,18 +571,24 @@ methods (Hidden) function projectObj = projectClass(obj) - % Converter routine from domainsClass to projectClass. - % This routine takes the currently defined project and - % converts it to a normal calculation, preserving all - % currently defined properties. + % Object Converter method to convert to projectClass + % https://uk.mathworks.com/help/matlab/matlab_oop/converting-objects-to-another-class.html. + % + % Examples + % -------- + % >>> domains = domainsClass(); + % >>> normalProject = projectClass(domains); % - % normalProject = project.projectClass(); + % Returns + % ------- + % projectObj : projectClass + % An instance of projectClass with the common properties copied from the domainsClass. projectObj = projectClass(obj.experimentName, obj.modelType, obj.geometry, obj.absorption); projectObj = copyProperties(obj, projectObj); % Need to treat contrasts separately due to changes in the % class for domains calculations - projectObj.contrasts = copyProperties(obj.contrasts, contrastsClass(domains=false)); + projectObj.contrasts = copyProperties(obj.contrasts, contrastsClass(false)); end end diff --git a/API/projectClass/projectClass.m b/API/projectClass/projectClass.m index bf78684e9..4cec4b1f0 100644 --- a/API/projectClass/projectClass.m +++ b/API/projectClass/projectClass.m @@ -17,32 +17,32 @@ % modelType : modelTypes, default: modelTypes.StandardLayers % The layer model type which can be 'standard layers', 'custom layers', or 'custom xy'. % geometry : geometryOptions, default: geometryOptions.AirSubstrate - % The geometry to use which can be 'air/substrate' or 'substrate/liquid'. + % The geometry to use which can be 'air/substrate' or 'substrate/liquid'. % absorption : logical, default: false % Indicates whether imaginary component is used for the SLD value in layers. % % Attributes % ---------- % parameters : parametersClass - % The project parameters. + % The project parameters. % bulkIn : parametersClass - % The bulkIn parameters. + % The bulkIn parameters. % bulkOut : parametersClass - % The bulkOut parameters. + % The bulkOut parameters. % scalefactors : parametersClass - % The scalefactors parameters. + % The scalefactors parameters. % layers : layersClass - % An object which contains the layers information. + % An object which contains the layers information. % data : dataClass - % An object which contains the data table. + % An object which contains the data table. % customFile : Custom file object - % An object which contains the defined customFiles. + % An object which contains the defined customFiles. % background : backgroundsClass object - % An object which contains defined backgrounds and background parameters. + % An object which contains defined backgrounds and background parameters. % resolution : resolutionClass object - % An object which contains defined resolutions and resolution parameters. + % An object which contains defined resolutions and resolution parameters. % contrasts : contrastsClass object - % An object which contains contrast information. + % An object which contains contrast information. properties experimentName @@ -176,14 +176,14 @@ function delete(obj) function names = getAllAllowedNames(obj) % Returns a struct with all currently set names of normal parameters, background and resolution % parameters, backgrounds, resolutions, bulk-ins, bulk-outs, scalefactors, data, custom files and - % contrasts for the project. + % contrast model for the project. % % Returns % ------- % names : struct % A struct with names of all the normal parameters, background and resolution % parameters, backgrounds, resolutions, bulk-ins, bulk-outs, scalefactors, data, custom files and - % contrasts entries in the project. + % contrast model entries in the project. names.paramNames = obj.parameters.getNames(); names.backgroundNames = obj.background.getNames(); names.backgroundParamNames = obj.background.backgroundParams.getNames(); @@ -1035,14 +1035,52 @@ function delete(obj) % ---------------------------------------------------------------- % Editing of Contrasts Block - function obj = addContrast(obj, varargin) - % Adds a new contrast parameter. Expects a parameter name, and with - % key-value pairs with one or more of the following "data", - % "background", "bulk in", "bulk out", "scalefactor", - % "resolution", "resample", "model" - % - % project.addContrast('contrast 1', 'bulkIn', 'Silicon'); - obj.contrasts.addContrast(obj.getAllAllowedNames(), varargin{:}); + function obj = addContrast(obj, options) + % Add a new contrast to the project. + % + % Examples + % -------- + % To add a new contrast with name only. + % + % >>> project.addContrast(name='new contrast'); + % + % To add a new contrast with other properties. + % + % >>> project.addContrast(name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', model={'Oxide', 'Bilayer tails'}); + % + % Parameters + % ---------- + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * model (cell) if this is a standard layers model, this should be a list of layer names that make up the slab model for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.model + end + args = namedargs2cell(options); + obj.contrasts.addContrast(obj.getAllAllowedNames(), args{:}); end function obj = removeContrast(obj, row) @@ -1066,30 +1104,83 @@ function delete(obj) obj.contrasts.removeContrast(row); end - function obj = setContrast(obj, row, varargin) - % Allow setting of all parameters in terms of name value pairs. - % First input must be contrast number or name, subsequent - % inputs are name / value pairs for the parts involved + function obj = setContrast(obj, row, options) + % General purpose method for updating properties of an existing contrast. % - % project.setContrast(1, 'name', 'contrast') - - % Get the list of allowed values depending on what is - % set for the other contrasts. - allowedValues = obj.getAllAllowedNames(); - - % Call the setContrast method - obj.contrasts.setContrast(row, allowedValues, varargin{:}); + % Examples + % -------- + % To change the properties of the second contrast in the object. + % + % >>> project.setContrast(2, name='new contrast', bulkIn='Silicon', bulkOut='D2O', background='D2O Background', ... + % resolution='Resolution 1', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O', model='Oxide Model'}); + % + % To change the properties of a contrast called 'Contrast 1'. + % + % >>> project.setContrast('Contrast 1', name='new contrast', scalefactor='Scalefactor 1', data='DSPC Bilayer D2O'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to update. If it is text, + % it is the name of the contrast to update. + % options + % Keyword/value pair to properties to update for the specific contrast. + % * name (char array or string, default: '') the name of the contrast. + % * data (char array or string, default: '') the name of the dataset parameter used by the contrast. + % * background (char array or string, default: '') the name of the background for the contrast. + % * backgroundAction (backgroundActions, default: backgroundActions.Add) whether the background should be added to the simulation ('add') or subtracted from the data ('subtract'). + % * bulkIn (char array or string, default: '') the name of the bulk-in parameter which defines the SLD of the interface between the first layer and the environment. + % * bulkOut (char array or string, default: '') the name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. + % * scalefactor (char array or string, default: '') the name of the scalefactor parameter which defines how much the data for this contrast should be scaled. + % * resolution (char array or string, default: '') the name of the instrument resolution for this contrast. + % * resample (logical, default: false) whether adaptive resampling should be used for interface microslicing. + % * repeatLayers (whole number, default: 1) indicates the number of times the layers should be repeated, this is only supported for standard layers. + % * model (char array or string or cell string) if this is a standard layers model, this should be a list of layer names that make up the slab model for this contrast. + % For custom models, this should be a single custom file name for the custom model function. + arguments + obj + row + options.name + options.data + options.background + options.backgroundAction + options.bulkIn + options.bulkOut + options.scalefactor + options.resolution + options.resample + options.repeatLayers + options.model + end + args = namedargs2cell(options); + obj.contrasts.setContrast(row, obj.getAllAllowedNames(), args{:}); end function obj = setContrastModel(obj, row, model) - % Edits the model of an existing contrast parameter. Expects - % the index of contrast parameter and cell array of layer - % names. Multiple models can be set simultaneously by using - % an 1D array of indices or cell of strings + % Updates the model of an existing contrast. + % + % Examples + % -------- + % To change the model of the second contrast in the project. + % + % >>> project.setContrastModel(2, 'Oxide Model'}); % - % project.setContrastModel(1, {'layer 1'}) - % project.setContrastModel(1:3, {'layer 1'}) - allowedValues = obj.getAllAllowedNames(); + % To change the properties of a contrast called 'Contrast 1'. + % + % >>> project.setContrastModel('Contrast 1', 'Oxide Model'); + % + % To change multiple contrasts at once. The snippet below will change 1, 2, and 3. + % + % >>> project.setContrastModel(1:3, {'Layer 1', 'Layer 2'}); + % + % Parameters + % ---------- + % row : string or char array or whole number + % If ``row`` is an integer, it is the row number of the contrast to update. If it is text, + % it is the name of the contrast to update. + % model: char array or string or cell string + % If this is a standard layers model, this should be a list of layer names that make up the slab model for this contrast. + % For custom models, this should be a single custom file name for the custom model function. % Call the setContrastModel method if isText(row) @@ -1098,7 +1189,7 @@ function delete(obj) row = num2cell(row); end for i=1:length(row) - obj.contrasts.setContrastModel(row{i}, allowedValues, model); + obj.setContrast(row{i}, model=model); end end @@ -1281,7 +1372,7 @@ function writeScript(obj, options) % Need to treat contrasts separately due to changes in the % class for domains calculations - domainsObj.contrasts = copyProperties(obj.contrasts, contrastsClass(domains=true)); + domainsObj.contrasts = copyProperties(obj.contrasts, contrastsClass(true)); for i=1:domainsObj.contrasts.numberOfContrasts domainsObj.contrasts.contrasts{i}.domainRatio = ''; end diff --git a/examples/tutorialFiles/twoContrastExample.mat b/examples/tutorialFiles/twoContrastExample.mat index 68490c406..6be93db4c 100644 Binary files a/examples/tutorialFiles/twoContrastExample.mat and b/examples/tutorialFiles/twoContrastExample.mat differ diff --git a/minimisers/DREAM/functions/metropolisRule.m b/minimisers/DREAM/functions/metropolisRule.m index 34dc1824c..ad21bd423 100755 --- a/minimisers/DREAM/functions/metropolisRule.m +++ b/minimisers/DREAM/functions/metropolisRule.m @@ -27,10 +27,10 @@ % Calculate the likelihood ratio alfa_L = exp ( log_L_xnew - log_L_xold ); - % Calculate the prior ration + % Calculate the prior ratio alfa_PR = exp ( log_PR_xnew - log_PR_xold ); - % Calculate product of two probabily ratios + % Calculate product of two probability ratios alfa = alfa_L .* alfa_PR; % Generate random numbers diff --git a/targetFunctions/common/callReflectivity/applyBackgroundCorrection.m b/targetFunctions/common/callReflectivity/applyBackgroundCorrection.m index 4c82990b7..ecd92b230 100644 --- a/targetFunctions/common/callReflectivity/applyBackgroundCorrection.m +++ b/targetFunctions/common/callReflectivity/applyBackgroundCorrection.m @@ -12,11 +12,11 @@ backError = background(lowIndex:highIndex,3); switch backgroundAction - case coderEnums.actions.Add + case coderEnums.backgroundActions.Add % Add the data to the simulation reflectivity(:,2) = reflectivity(:,2) + backData; simulation(:,2) = simulation(:,2) + background(:,2); - case coderEnums.actions.Subtract + case coderEnums.backgroundActions.Subtract % Subtract the background data from the shiftedData shiftedData(:,2) = shiftedData(:,2) - backData; shiftedData(:,3) = sqrt(shiftedData(:,3).^2 + backError.^2); % Propagate the errors diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomLayers.m b/tests/domainsTFReflectivityCalculation/domainsCustomLayers.m index 6a05f266e..ef13b9578 100644 --- a/tests/domainsTFReflectivityCalculation/domainsCustomLayers.m +++ b/tests/domainsTFReflectivityCalculation/domainsCustomLayers.m @@ -25,7 +25,7 @@ project.addContrast('name', 'D2O Conrast', ... 'Data', 'Simulation',... 'Background', 'Background 1',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'bulkIn', 'Silicon',... 'bulkOut', 'SLD D2O',... 'Scalefactor','Scalefactor 1', ... diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomLayersInputs.mat b/tests/domainsTFReflectivityCalculation/domainsCustomLayersInputs.mat index 221b46540..758dd6ff5 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomLayersInputs.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomLayersInputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomLayersOutputs.mat b/tests/domainsTFReflectivityCalculation/domainsCustomLayersOutputs.mat index ed60930fc..0741c240e 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomLayersOutputs.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomLayersOutputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomLayersTFParams.mat b/tests/domainsTFReflectivityCalculation/domainsCustomLayersTFParams.mat index affb12f4e..a147cc261 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomLayersTFParams.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomLayersTFParams.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomXY.m b/tests/domainsTFReflectivityCalculation/domainsCustomXY.m index 8dd1afe32..bd841dfb7 100644 --- a/tests/domainsTFReflectivityCalculation/domainsCustomXY.m +++ b/tests/domainsTFReflectivityCalculation/domainsCustomXY.m @@ -39,7 +39,7 @@ project.addContrast('name','Test',... 'background','Background D2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Air',.... diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomXYInputs.mat b/tests/domainsTFReflectivityCalculation/domainsCustomXYInputs.mat index 423298ca4..d1eec25bc 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomXYInputs.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomXYInputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomXYOutputs.mat b/tests/domainsTFReflectivityCalculation/domainsCustomXYOutputs.mat index 10882ce7d..6b330ffa9 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomXYOutputs.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomXYOutputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsCustomXYTFParams.mat b/tests/domainsTFReflectivityCalculation/domainsCustomXYTFParams.mat index 66a2121ed..df039e85f 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsCustomXYTFParams.mat and b/tests/domainsTFReflectivityCalculation/domainsCustomXYTFParams.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsStandardLayers.m b/tests/domainsTFReflectivityCalculation/domainsStandardLayers.m index 34fb7674d..4f3ad7881 100644 --- a/tests/domainsTFReflectivityCalculation/domainsStandardLayers.m +++ b/tests/domainsTFReflectivityCalculation/domainsStandardLayers.m @@ -33,15 +33,15 @@ project.addLayerGroup({Layer1, Layer2}); - project.addDomainContrast('Domain1'); - project.addDomainContrast('Domain2'); + project.addDomainContrast(name='Domain1'); + project.addDomainContrast(name='Domain2'); project.setDomainContrastModel(1,'Domain1 Layer'); project.setDomainContrastModel(2,'Domain2 Layer'); project.addContrast('name','Domain Test',... 'background','Background 1',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'resample',false,... diff --git a/tests/domainsTFReflectivityCalculation/domainsStandardLayersInputs.mat b/tests/domainsTFReflectivityCalculation/domainsStandardLayersInputs.mat index 16962d302..f2184ba80 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsStandardLayersInputs.mat and b/tests/domainsTFReflectivityCalculation/domainsStandardLayersInputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsStandardLayersOutputs.mat b/tests/domainsTFReflectivityCalculation/domainsStandardLayersOutputs.mat index 2fef4f001..f70578737 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsStandardLayersOutputs.mat and b/tests/domainsTFReflectivityCalculation/domainsStandardLayersOutputs.mat differ diff --git a/tests/domainsTFReflectivityCalculation/domainsStandardLayersTFParams.mat b/tests/domainsTFReflectivityCalculation/domainsStandardLayersTFParams.mat index 9ea9950f8..42ad6f07b 100644 Binary files a/tests/domainsTFReflectivityCalculation/domainsStandardLayersTFParams.mat and b/tests/domainsTFReflectivityCalculation/domainsStandardLayersTFParams.mat differ diff --git a/tests/normalTFReflectivityCalculation/DPPCCustomXYScript.m b/tests/normalTFReflectivityCalculation/DPPCCustomXYScript.m index e952c7047..4a4fa8592 100644 --- a/tests/normalTFReflectivityCalculation/DPPCCustomXYScript.m +++ b/tests/normalTFReflectivityCalculation/DPPCCustomXYScript.m @@ -63,7 +63,7 @@ % D2O contrast.. project.addContrast('name','Bilayer / D2O',... 'background','Background D2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... @@ -73,7 +73,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / SMW',... 'background','Background SMW',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... @@ -83,7 +83,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / H2O',... 'background','Background H2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... diff --git a/tests/normalTFReflectivityCalculation/DPPCStandardLayers.m b/tests/normalTFReflectivityCalculation/DPPCStandardLayers.m index 2b1333d52..a4b33dd89 100644 --- a/tests/normalTFReflectivityCalculation/DPPCStandardLayers.m +++ b/tests/normalTFReflectivityCalculation/DPPCStandardLayers.m @@ -193,7 +193,7 @@ % D2O contrast.. project.addContrast('name','Bilayer / D2O',... 'background','Background D2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'resample',false,.... @@ -204,7 +204,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / SMW',... 'background','Background SMW',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'resample',false,.... @@ -215,7 +215,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / H2O',... 'background','Background H2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'resample',false,... diff --git a/tests/normalTFReflectivityCalculation/absorptionInputs.mat b/tests/normalTFReflectivityCalculation/absorptionInputs.mat index 83584ff53..30a2f73e4 100644 Binary files a/tests/normalTFReflectivityCalculation/absorptionInputs.mat and b/tests/normalTFReflectivityCalculation/absorptionInputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/absorptionOutputs.mat b/tests/normalTFReflectivityCalculation/absorptionOutputs.mat index 2fff052a6..839b4a6d4 100644 Binary files a/tests/normalTFReflectivityCalculation/absorptionOutputs.mat and b/tests/normalTFReflectivityCalculation/absorptionOutputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/absorptionTFParams.mat b/tests/normalTFReflectivityCalculation/absorptionTFParams.mat index c396c81c4..af87c5744 100644 Binary files a/tests/normalTFReflectivityCalculation/absorptionTFParams.mat and b/tests/normalTFReflectivityCalculation/absorptionTFParams.mat differ diff --git a/tests/normalTFReflectivityCalculation/customLayersInputs.mat b/tests/normalTFReflectivityCalculation/customLayersInputs.mat index 7e0d7fb0f..82c8ed853 100644 Binary files a/tests/normalTFReflectivityCalculation/customLayersInputs.mat and b/tests/normalTFReflectivityCalculation/customLayersInputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/customLayersOutputs.mat b/tests/normalTFReflectivityCalculation/customLayersOutputs.mat index a3240e6fe..71692fa7c 100644 Binary files a/tests/normalTFReflectivityCalculation/customLayersOutputs.mat and b/tests/normalTFReflectivityCalculation/customLayersOutputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/customLayersTFParams.mat b/tests/normalTFReflectivityCalculation/customLayersTFParams.mat index e354c0210..1298ae563 100644 Binary files a/tests/normalTFReflectivityCalculation/customLayersTFParams.mat and b/tests/normalTFReflectivityCalculation/customLayersTFParams.mat differ diff --git a/tests/normalTFReflectivityCalculation/customXYInputs.mat b/tests/normalTFReflectivityCalculation/customXYInputs.mat index 39c0c5666..d26116d8d 100644 Binary files a/tests/normalTFReflectivityCalculation/customXYInputs.mat and b/tests/normalTFReflectivityCalculation/customXYInputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/customXYOutputs.mat b/tests/normalTFReflectivityCalculation/customXYOutputs.mat index 63d676138..25c583f0f 100644 Binary files a/tests/normalTFReflectivityCalculation/customXYOutputs.mat and b/tests/normalTFReflectivityCalculation/customXYOutputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/customXYTFParams.mat b/tests/normalTFReflectivityCalculation/customXYTFParams.mat index 0bd167f26..e920f8df9 100644 Binary files a/tests/normalTFReflectivityCalculation/customXYTFParams.mat and b/tests/normalTFReflectivityCalculation/customXYTFParams.mat differ diff --git a/tests/normalTFReflectivityCalculation/orsoDSPCCustomLayers.m b/tests/normalTFReflectivityCalculation/orsoDSPCCustomLayers.m index ad167db36..8cf51508a 100644 --- a/tests/normalTFReflectivityCalculation/orsoDSPCCustomLayers.m +++ b/tests/normalTFReflectivityCalculation/orsoDSPCCustomLayers.m @@ -105,7 +105,7 @@ % D2O contrast.. project.addContrast('name','Bilayer / D2O',... 'background','Background D2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... @@ -115,7 +115,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / SMW',... 'background','Background SMW',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... @@ -125,7 +125,7 @@ % SMW contrast.. project.addContrast('name','Bilayer / H2O',... 'background','Background H2O',... - 'backgroundAction', actions.Add,... + 'backgroundAction', backgroundActions.Add,... 'resolution','Resolution 1',... 'scalefactor', 'Scalefactor 1',... 'bulkIn', 'Silicon',... diff --git a/tests/normalTFReflectivityCalculation/standardLayersInputs.mat b/tests/normalTFReflectivityCalculation/standardLayersInputs.mat index d27278cf6..881c9b39f 100644 Binary files a/tests/normalTFReflectivityCalculation/standardLayersInputs.mat and b/tests/normalTFReflectivityCalculation/standardLayersInputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/standardLayersOutputs.mat b/tests/normalTFReflectivityCalculation/standardLayersOutputs.mat index 63e6ad152..13a5698d1 100644 Binary files a/tests/normalTFReflectivityCalculation/standardLayersOutputs.mat and b/tests/normalTFReflectivityCalculation/standardLayersOutputs.mat differ diff --git a/tests/normalTFReflectivityCalculation/standardLayersTFParams.mat b/tests/normalTFReflectivityCalculation/standardLayersTFParams.mat index 3e0933970..235aec75a 100644 Binary files a/tests/normalTFReflectivityCalculation/standardLayersTFParams.mat and b/tests/normalTFReflectivityCalculation/standardLayersTFParams.mat differ diff --git a/tests/testContrastsClass.m b/tests/testContrastsClass.m index c474a2ee9..73942d5d5 100644 --- a/tests/testContrastsClass.m +++ b/tests/testContrastsClass.m @@ -11,14 +11,13 @@ properties (TestParameter) contrastInput = {{}, ... - {'Named Contrast'}, ... {'name', 'Few params', ... 'background', 'Background D2O', ... 'resolution', 'Resolution 1'}, ... {'name', 'All params', ... 'data', 'Simulation', ... 'background', 'Background H2O', ... - 'backgroundAction', actions.Subtract, ... + 'backgroundAction', backgroundActions.Subtract, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD H2O', ... 'scalefactor', 'Scalefactor 1', ... @@ -36,7 +35,6 @@ } removeInput = {1, 'bilayer / d2o'} changedFields = {{{'name', 'New contrast 1'}}, ... - {{'name', 'Named Contrast'}}, ... {{'name', 'Few params'}, ... {'background', 'Background D2O'}, ... {'resolution', 'Resolution 1'} @@ -66,10 +64,10 @@ numContrasts % Number of Contrasts defined in exampleClass % Define the layers from "DPPC_standard_layers.m" - layerNames = ["Oxide Layer" "Water Layer" "Bil inner head" "Bil tail" "Bil outer head"] + layerNames = ["Oxide Layer", "Water Layer", "Bil inner head", "Bil tail", "Bil outer head"] % Define the custom files from "orsoDSPC_custLay_script.m" and % "DPPC_customXY.m" - customNames = ["DSPC Model" "DPPC Model"] + customNames = ["DSPC Model", "DPPC Model"] domainContrastNames = ["Domain Contrast 1", "Domain Contrast 2", "Test Domain Contrast"] % Data from data class @@ -103,7 +101,7 @@ function initialiseAllowedNames(testCase) % resolution and domain ratios testCase.allowedNames = struct( ... 'backgroundNames', ["Background D2O" "Background SMW" "Background H2O"], ... - 'bulkInNames', 'Silicon', ... + 'bulkInNames', "Silicon", ... 'bulkOutNames', ["SLD D2O" "SLD SMW" "SLD H2O"], ... 'resolutionNames', ["Resolution 1" "Test Resolution"], ... 'layerNames', testCase.layerNames, ... @@ -136,7 +134,7 @@ function initialiseDefaultContrastParams(testCase) 'name', '', ... 'data', '', ... 'background', '', ... - 'backgroundAction', actions.Add.value, ... + 'backgroundAction', backgroundActions.Add.value, ... 'bulkIn', '', ... 'bulkOut', '', ... 'scalefactor', '', ... @@ -154,7 +152,7 @@ function initialiseNewValues(testCase) 'name', 'New Contrast', ... 'data', 'Simulation', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Subtract, ... + 'backgroundAction', backgroundActions.Subtract, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Test Scalefactor', ... @@ -174,13 +172,13 @@ function initialiseContrastsClass(testCase) % Set up an example contrasts class for testing % This example is used in the example calculation % "DPPC_standard_layers.m" - testCase.exampleClass = contrastsClass(domains=true); + testCase.exampleClass = contrastsClass(true); testCase.exampleClass.contrasts(1) = {struct( ... 'name', 'Bilayer / D2O', ... 'data', 'Bilayer / D2O', ... 'background', 'Background D2O', ... - 'backgroundAction', actions.Add.value, ... + 'backgroundAction', backgroundActions.Add.value, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD D2O', ... 'scalefactor', 'Scalefactor 1', ... @@ -195,7 +193,7 @@ function initialiseContrastsClass(testCase) 'name', 'Bilayer / SMW', ... 'data', 'Bilayer / SMW', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Add.value, ... + 'backgroundAction', backgroundActions.Add.value, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Scalefactor 1', ... @@ -210,7 +208,7 @@ function initialiseContrastsClass(testCase) 'name', 'Bilayer / H2O', ... 'data', 'Bilayer / H2O', ... 'background', 'Background H2O', ... - 'backgroundAction', actions.Add.value, ... + 'backgroundAction', backgroundActions.Add.value, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD H2O', ... 'scalefactor', 'Scalefactor 1', ... @@ -261,18 +259,18 @@ function testInitialiseContrastsClass(testCase) % Standard calculation testClass = contrastsClass(); testCase.verifyEqual(testClass.contrasts, {}, 'contrastsClass does not initialise correctly'); - testCase.verifyFalse(testClass.domainsCalc); + testCase.verifyFalse(testClass.isDomains); % Domains Calculation - testDomainsClass = contrastsClass(domains=true); + testDomainsClass = contrastsClass(true); testCase.verifyEqual(testDomainsClass.contrasts, {}, 'contrastsClass does not initialise correctly'); - testCase.verifyTrue(testDomainsClass.domainsCalc); + testCase.verifyTrue(testDomainsClass.isDomains); end function testInitialiseContrastsClassIllogical(testCase) % If we initialise a contrasts class with a non-logical % variable we should fail validation - testCase.verifyError(@() contrastsClass(domains=0), 'MATLAB:validators:mustBeA'); + testCase.verifyError(@() contrastsClass(0), 'MATLAB:validators:mustBeA'); end function testAddContrast(testCase, contrastInput, changedFields) @@ -326,10 +324,10 @@ function testSetContrastModelStandardLayersDomains(testCase) contrastIndex = 1; testModel = {'Domain Contrast 1', 'Test Domain Contrast'}; - testCase.exampleClass.setContrastModel(contrastIndex, testCase.allowedNames, testModel); + testCase.exampleClass.setContrast(contrastIndex, testCase.allowedNames, model=testModel); testCase.verifyEqual(testCase.exampleClass.contrasts{contrastIndex}.model, testModel, 'setContrastModel does not work correctly'); end - + function testSetContrastModelStandardLayers(testCase) % Test setting a model for a contrast from the contrasts class % for a "standard layers" model type @@ -339,12 +337,12 @@ function testSetContrastModelStandardLayers(testCase) testModel = {'Oxide Layer', 'Water Layer'}; testCase.allowedNames.modelNames = testCase.layerNames; - noDomainsClass.setContrastModel(1, testCase.allowedNames, testModel); + noDomainsClass.setContrast(1, testCase.allowedNames, model=testModel); testCase.verifyEqual(noDomainsClass.contrasts{1}.model, testModel, 'setContrastModel does not work correctly'); testCase.allowedNames.layerNames = {}; - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, {'Oxide Layer'}), exceptions.invalidValue.errorID); - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, {'DPPC Model'}), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model={'Oxide Layer'}), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model={'DPPC Model'}), exceptions.invalidValue.errorID); end @@ -358,14 +356,15 @@ function testSetContrastModelCustomLayers(testCase) testModel = {'DPPC Model'}; testCase.allowedNames.modelNames = testCase.customNames; - testCase.exampleClass.setContrastModel(1, testCase.allowedNames, testModel); + testCase.exampleClass.setContrast(1, testCase.allowedNames, model=testModel); testCase.verifyEqual(testCase.exampleClass.contrasts{1}.model, testModel, 'setContrastModel does not work correctly'); % only one model value is allowed - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, {'DPPC Model', 'DSPC Model'}), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model={'DPPC Model', 'DSPC Model'}), exceptions.invalidValue.errorID); testCase.allowedNames.customFileNames = {}; - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, {'DPPC Model'}), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model={'DPPC Model'}), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1.5, testCase.allowedNames, model={'DPPC Model'}), exceptions.invalidType.errorID); end function testSetContrastModelInvalid(testCase) @@ -373,22 +372,22 @@ function testSetContrastModelInvalid(testCase) % If the input is invalid we should raise an error % Contrast must be recognisable by name or index - testCase.verifyError(@() testCase.exampleClass.setContrastModel(0, testCase.allowedNames, {'Oxide Layer'}), exceptions.indexOutOfRange.errorID); - testCase.verifyError(@() testCase.exampleClass.setContrastModel(testCase.numContrasts+1, testCase.allowedNames, {'Oxide Layer'}), exceptions.indexOutOfRange.errorID); - testCase.verifyError(@() testCase.exampleClass.setContrastModel('Invalid Contrast', testCase.allowedNames, {'Oxide Layer'}), exceptions.nameNotRecognised.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(0, testCase.allowedNames, model={'Oxide Layer'}), exceptions.indexOutOfRange.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(testCase.numContrasts+1, testCase.allowedNames, model={'Oxide Layer'}), exceptions.indexOutOfRange.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast('Invalid Contrast', testCase.allowedNames, model={'Oxide Layer'}), exceptions.nameNotRecognised.errorID); % Contrast models must be defined in allowed values, and only % one model is allowed for "custom layers" and "custom XY" testModel = {'Domain Contrast 1', 'Invalid Domain Contrast'}; - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, testModel), exceptions.nameNotRecognised.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model=testModel), exceptions.nameNotRecognised.errorID); % Only two contrast models are allowed for "standard layers" % with domains testModel = {'Domain Contrast 1', 'Domain Contrast 2', 'Test Domain Contrast'}; - testCase.verifyError(@() testCase.exampleClass.setContrastModel(1, testCase.allowedNames, testModel), exceptions.invalidValue.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, model=testModel), exceptions.invalidValue.errorID); % Partial matches must be disallowed - testCase.verifyError(@() testCase.exampleClass.setContrastModel('Oxide Lay', testCase.allowedNames, {'Oxide Layer'}), exceptions.nameNotRecognised.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast('Oxide Lay', testCase.allowedNames, model={'Oxide Layer'}), exceptions.nameNotRecognised.errorID); end function testSetContrast(testCase) @@ -398,7 +397,7 @@ function testSetContrast(testCase) 'name', 'New Contrast', ... 'data', 'Simulation', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Subtract.value, ... + 'backgroundAction', backgroundActions.Subtract.value, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Test Scalefactor', ... @@ -427,7 +426,7 @@ function testSetContrastNoDomains(testCase) 'name', 'No Domains Contrast', ... 'data', 'Simulation', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Add, ... + 'backgroundAction', backgroundActions.Add, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Test Scalefactor', ... @@ -440,7 +439,7 @@ function testSetContrastNoDomains(testCase) 'name', 'No Domains Contrast', ... 'data', 'Simulation', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Add.value, ... + 'backgroundAction', backgroundActions.Add.value, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Test Scalefactor', ... @@ -463,17 +462,20 @@ function testSetContrastNoDomains(testCase) function testSetContrastInvalid(testCase) % Test setting parameter values within a contrast % Contrast must be recognisable by name or index - testCase.verifyError(@() testCase.exampleClass.setContrast(0, testCase.allowedNames, testCase.newValues), exceptions.indexOutOfRange.errorID); - testCase.verifyError(@() testCase.exampleClass.setContrast(testCase.numContrasts+1, testCase.allowedNames, testCase.newValues), exceptions.indexOutOfRange.errorID); - testCase.verifyError(@() testCase.exampleClass.setContrast('Invalid Contrast', testCase.allowedNames, testCase.newValues), exceptions.nameNotRecognised.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(0, testCase.allowedNames, testCase.newValues{:}), exceptions.indexOutOfRange.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast(testCase.numContrasts+1, testCase.allowedNames, testCase.newValues{:}), exceptions.indexOutOfRange.errorID); + testCase.verifyError(@() testCase.exampleClass.setContrast('Invalid Contrast', testCase.allowedNames, testCase.newValues{:}), exceptions.nameNotRecognised.errorID); % "backgroundAction" should be a value of the "actions" enum testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, 'backgroundAction', 'random'), exceptions.invalidOption.errorID); % Partial matches of fields names and parameter values should % be disallowed - testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, 'backgr', 'Background H2O'), 'MATLAB:InputParser:UnmatchedParameter'); + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, 'backgr', 'Background H2O'), 'MATLAB:functionValidation:AmbiguousInputName'); testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, 'background', 'Background H'), exceptions.nameNotRecognised.errorID); + testCase.allowedNames.backgroundNames = {}; + testCase.verifyError(@() testCase.exampleClass.setContrast(1, testCase.allowedNames, 'background', 'Background H'), exceptions.nameNotRecognised.errorID); + end function testSetContrastWarning(testCase) @@ -485,7 +487,7 @@ function testSetContrastWarning(testCase) function testGetAllContrastNames(testCase) - testCase.verifyEqual(testCase.exampleClass.getAllContrastNames, {'Bilayer / D2O', 'Bilayer / SMW', 'Bilayer / H2O'}, 'getAllContrastNames does not work correctly'); + testCase.verifyEqual(testCase.exampleClass.getNames, {'Bilayer / D2O'; 'Bilayer / SMW'; 'Bilayer / H2O'}, 'getAllContrastNames does not work correctly'); end function testUpdateDataName(testCase) @@ -549,7 +551,41 @@ function testToStructCustomLayers(testCase) testCase.verifyEqual(testCase.exampleClass.toStruct(testCase.allowedNames, 'custom layers', testCase.varTable), testCase.exampleStruct); end - function testToStructDisallowedData(testCase) + function testToStructMissingData(testCase) + % Test converting the contrasts class to a struct with missing + % parameters throws exception. + contrastIndex = 2; + oldContrast = testCase.exampleClass.contrasts{contrastIndex}; + inputs = {'data', 'background', 'resolution', 'domainRatio', 'bulkIn', 'bulkOut', 'scalefactor'}; + for i=1:length(inputs) + testCase.exampleClass.contrasts{contrastIndex} = oldContrast; + testCase.exampleClass.contrasts{contrastIndex}.(inputs{i}) = 'bad name'; + testCase.verifyError(@() testCase.exampleClass.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), exceptions.nameNotRecognised.errorID); + end + + testCase.exampleClass.contrasts{contrastIndex} = oldContrast; + testCase.exampleClass.contrasts{contrastIndex}.model{1} = 'bad domain contrast name'; + testCase.verifyError(@() testCase.exampleClass.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), exceptions.nameNotRecognised.errorID); + + testCase.exampleClass.contrasts{contrastIndex} = oldContrast; + testCase.exampleClass.contrasts{contrastIndex}.model{1} = 'bad file name'; + testCase.verifyError(@() testCase.exampleClass.toStruct(testCase.allowedNames, 'custom layers', testCase.varTable), exceptions.nameNotRecognised.errorID); + + nonDomainContrast = contrastsClass(); + nonDomainContrast.contrasts(1) = {struct('name', '', 'model', {{'Oxide Layer', 'bad Layers name'}})}; + testCase.verifyError(@() nonDomainContrast.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), exceptions.nameNotRecognised.errorID); + + testCase.exampleClass.contrasts{contrastIndex} = oldContrast; + testCase.exampleClass.contrasts{contrastIndex}.background = ''; + testCase.verifyError(@() testCase.exampleClass.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), exceptions.invalidValue.errorID); + + testCase.exampleClass.contrasts{contrastIndex} = oldContrast; + testCase.allowedNames.backgroundNames = {}; + testCase.verifyError(@() testCase.exampleClass.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), exceptions.nameNotRecognised.errorID); + + end + + function testToStructEmptyData(testCase) % Test converting the contrasts class to a struct % If the data parameter is not in the allowed names, set some % dummy values in the struct @@ -557,11 +593,11 @@ function testToStructDisallowedData(testCase) % Modify exampleClass and exampleStruct to remove a data entry % and insert the expected dummy values in the struct - testCase.exampleClass.contrasts{contrastIndex}.data = 'Disallowed data name'; + testCase.exampleClass.contrasts{contrastIndex}.data = ''; testCase.exampleStruct.dataPresent(contrastIndex) = 0; testCase.exampleStruct.dataLimits{contrastIndex} = [0 0]; - testCase.exampleStruct.simulationLimits{contrastIndex} = [0 0]; + testCase.exampleStruct.simulationLimits{contrastIndex} = dataClass.defaultSimRange; testCase.exampleStruct.contrastData{contrastIndex} = [0 0 0]; testCase.verifyEqual(testCase.exampleClass.toStruct(testCase.allowedNames, 'standard layers', testCase.varTable), testCase.exampleStruct); @@ -727,7 +763,7 @@ function testParseContrastInput(testCase) 'name', 'New Contrast', ... 'data', 'Simulation', ... 'background', 'Background SMW', ... - 'backgroundAction', actions.Subtract, ... + 'backgroundAction', backgroundActions.Subtract, ... 'bulkIn', 'Silicon', ... 'bulkOut', 'SLD SMW', ... 'scalefactor', 'Test Scalefactor', ... @@ -749,7 +785,7 @@ function testParseContrastInputEmpty(testCase) % Test parsing input data for a contrast within the contrasts % class. % If the input is empty, we should return the default values - emptyContrasts = contrastsClass(domains=true); + emptyContrasts = contrastsClass(true); % Need to change default contrast fields to empty defaults used % in parser diff --git a/tests/testCreateProject.m b/tests/testCreateProject.m index 202d5ea30..faa9c3ab5 100644 --- a/tests/testCreateProject.m +++ b/tests/testCreateProject.m @@ -32,7 +32,7 @@ function testProjectTypes(testCase, calculationType, SLDValues, domainsCalc, abs % Test for correct layers and contrasts layerColumns = [{'Name', 'Thickness'}, SLDValues, {'Roughness', 'Hydration', 'Hydrate with'}]; testCase.verifyEqual(project.layers.varTable.Properties.VariableNames, layerColumns, 'project layers class does not initialise correctly'); - testCase.verifyEqual(project.contrasts.domainsCalc, domainsCalc, 'project contrasts class does not initialise correctly'); + testCase.verifyEqual(project.contrasts.isDomains, domainsCalc, 'project contrasts class does not initialise correctly'); % Test setting experiment name newName = 'New Project Name'; diff --git a/tests/testDomainContrastsClass.m b/tests/testDomainContrastsClass.m index 247e3cba0..7501b0ee8 100644 --- a/tests/testDomainContrastsClass.m +++ b/tests/testDomainContrastsClass.m @@ -100,7 +100,7 @@ function initialiseContrastsStruct(testCase) function testInitialiseDomainContrastsClass(testCase) testClass = domainContrastsClass(); testCase.verifyEqual(testClass.contrasts, {}, 'domainContrastsClass does not initialise correctly'); - testCase.verifyFalse(testClass.domainsCalc); + testCase.verifyFalse(testClass.isDomains); end function testToStruct(testCase) diff --git a/tests/testDomainsClass.m b/tests/testDomainsClass.m index d48c5f33e..0fc622465 100644 --- a/tests/testDomainsClass.m +++ b/tests/testDomainsClass.m @@ -89,13 +89,13 @@ function testConversion(testCase) % the properties are copied over testCase.verifyClass(testCase.project, 'domainsClass') testCase.verifyEqual(testCase.project.calculationType, calculationTypes.Domains.value, 'Calculation Type not set correctly'); - testCase.verifyTrue(testCase.project.contrasts.domainsCalc, 'Calculation Type not set correctly') + testCase.verifyTrue(testCase.project.contrasts.isDomains, 'Calculation Type not set correctly') normal = testCase.project.toProjectClass(); testCase.verifyClass(normal, 'projectClass') testCase.verifyEqual(normal.experimentName, testCase.project.experimentName, 'Experiment name not copied correctly'); testCase.verifyEqual(normal.calculationType, calculationTypes.Normal.value, 'Calculation Type not set correctly'); - testCase.verifyFalse(normal.contrasts.domainsCalc, 'Calculation Type not set correctly') + testCase.verifyFalse(normal.contrasts.isDomains, 'Calculation Type not set correctly') testCase.verifyEqual(normal.parameters, testCase.project.parameters, 'Parameters not copied correctly'); testCase.verifyEqual(normal.layers, testCase.project.layers, 'Layers not copied correctly'); @@ -155,11 +155,29 @@ function testDomainRatio(testCase) testCase.project.setDomainRatio(1, 'name','Domain Ratio 1','min',0.1,'value',0.23251,'max',0.4,'fit',true); testCase.verifyEqual(string(testCase.project.domainRatio.varTable{1, :}),... string({'Domain Ratio 1', 0.1, 0.23251, 0.4, true, priorTypes.Uniform.value, 0, Inf}), 'domainRatio method not working'); + testCase.project.addDomainRatio(); + testCase.verifyEqual(string(testCase.project.domainRatio.varTable{end, 1}), "new parameter 4"); + testCase.project.setDomainRatio(3, 'min', -0.5); + testCase.verifyEqual(testCase.project.domainRatio.varTable{3, 2:4}, [-0.5, 0, 0]); + + end + + function testSetContrast(testCase) + % Populates project properties for the tests + testCase.populateProject(); + testCase.project.addDomainContrast('name', 'Domains 1'); + testCase.project.addDomainContrast('name', 'Domains 2'); + testCase.project.addContrast('name', 'Bilayer / H2O'); + testCase.project.addContrast('name', 'Bilayer / D2O'); + testCase.project.setContrastModel(1:2, {'Domains 1', 'Domains 2'}); + testCase.verifyLength(testCase.project.contrasts.contrasts, 2, 'contrast has wrong dimension'); + testCase.verifyEqual(testCase.project.contrasts.contrasts{1}.model, {'Domains 1', 'Domains 2'}, 'addContrast method not working'); + testCase.verifyEqual(testCase.project.contrasts.contrasts{2}.model, {'Domains 1', 'Domains 2'}, 'setContrastModel method not working'); end function testDomainContrast(testCase) % Populates project properties for the tests - testCase.populateProject() + testCase.populateProject(); % Checks the default contrast testCase.verifyEmpty(testCase.project.domainContrasts.contrasts, 'contrast has wrong dimension'); % Checks that contrast can be added @@ -202,7 +220,6 @@ function testDomainContrastExceptions(testCase) testCase.verifyError(@() customProject.setDomainContrastModel(1, {'Hydrogenated Heads', 'Deuterated Heads'}), exceptions.invalidProperty.errorID) end - function testToStruct(testCase) projectStruct = testCase.project.toStruct(); testCase.verifyEqual(projectStruct.experimentName, testCase.project.experimentName, 'toStruct method not working'); diff --git a/tests/testExamples.m b/tests/testExamples.m index 408c593de..094ea06fd 100644 --- a/tests/testExamples.m +++ b/tests/testExamples.m @@ -209,7 +209,7 @@ function testWriteScript(testCase, exampleScriptFile) testCase.verifyEqual(problem.contrasts.contrasts{i}, scriptProblem.contrasts.contrasts{i}, sprintf('contrast %d is not correctly regenerated by the script', i)) end - testCase.verifyEqual(problem.contrasts.domainsCalc, scriptProblem.contrasts.domainsCalc, 'contrasts domainsCalc is not correctly regenerated by the script') + testCase.verifyEqual(problem.contrasts.isDomains, scriptProblem.contrasts.isDomains, 'contrasts domainsCalc is not correctly regenerated by the script') testCase.verifyEqual(problem.contrasts.displayNames, scriptProblem.contrasts.displayNames, 'contrasts displayNames is not correctly regenerated by the script') end end diff --git a/tests/testProjectClass.m b/tests/testProjectClass.m index 269188c70..bea3de463 100644 --- a/tests/testProjectClass.m +++ b/tests/testProjectClass.m @@ -108,13 +108,13 @@ function testConversion(testCase) % the properties are copied over testCase.verifyClass(testCase.project, 'projectClass') testCase.verifyEqual(testCase.project.calculationType, calculationTypes.Normal.value, 'Calculation Type not set correctly'); - testCase.verifyFalse(testCase.project.contrasts.domainsCalc, 'Calculation Type not set correctly') + testCase.verifyFalse(testCase.project.contrasts.isDomains, 'Calculation Type not set correctly') domains = testCase.project.toDomainsClass(); testCase.verifyClass(domains, 'domainsClass') testCase.verifyEqual(domains.experimentName, testCase.project.experimentName, 'Experiment name not copied correctly'); testCase.verifyEqual(domains.calculationType, calculationTypes.Domains.value, 'Calculation Type not set correctly'); - testCase.verifyTrue(domains.contrasts.domainsCalc, 'Calculation Type not set correctly') + testCase.verifyTrue(domains.contrasts.isDomains, 'Calculation Type not set correctly') testCase.verifyEqual(domains.parameters, testCase.project.parameters, 'Parameters not copied correctly'); testCase.verifyEqual(domains.layers, testCase.project.layers, 'Layers not copied correctly'); @@ -543,7 +543,7 @@ function testContrast(testCase) testCase.verifyEqual(testCase.project.contrasts.contrasts{1}.model, {'Hydrogenated Heads', 'Deuterated Heads'}, 'setContrastModel method not working'); testCase.project.setContrastModel('Another Bilayer', {'Deuterated Heads', 'Hydrogenated Heads'}); testCase.verifyEqual(testCase.project.contrasts.contrasts{1}.model, {'Deuterated Heads', 'Hydrogenated Heads'}, 'setContrastModel method not working'); - testCase.project.addContrast('First Bilayer'); + testCase.project.addContrast('name', 'First Bilayer'); testCase.project.setContrastModel(1:2, {'Deuterated Heads'}); testCase.verifyEqual(testCase.project.contrasts.contrasts{1}.model, {'Deuterated Heads'}, 'setContrastModel method not working'); testCase.verifyEqual(testCase.project.contrasts.contrasts{2}.model, {'Deuterated Heads'}, 'setContrastModel method not working'); @@ -652,7 +652,7 @@ function testWriteScript(testCase) testCase.verifyEqual(testCase.project.contrasts.contrasts{i}, problem.contrasts.contrasts{i}, sprintf('contrast %d is not correctly regenerated by the script', i)); end - testCase.verifyEqual(testCase.project.contrasts.domainsCalc, problem.contrasts.domainsCalc, 'contrasts domainsCalc is not correctly regenerated by the script'); + testCase.verifyEqual(testCase.project.contrasts.isDomains, problem.contrasts.isDomains, 'contrasts domainsCalc is not correctly regenerated by the script'); testCase.verifyEqual(testCase.project.contrasts.displayNames, problem.contrasts.displayNames, 'contrasts displayNames is not correctly regenerated by the script'); end diff --git a/tests/testProjectConversion/DSPCBilayerProjectClass.mat b/tests/testProjectConversion/DSPCBilayerProjectClass.mat index 3b1d90d47..3cded951a 100644 Binary files a/tests/testProjectConversion/DSPCBilayerProjectClass.mat and b/tests/testProjectConversion/DSPCBilayerProjectClass.mat differ diff --git a/tests/testProjectConversion/monolayerVolumeModelProjectClass.mat b/tests/testProjectConversion/monolayerVolumeModelProjectClass.mat index b00d2036d..84f9dfa8c 100644 Binary files a/tests/testProjectConversion/monolayerVolumeModelProjectClass.mat and b/tests/testProjectConversion/monolayerVolumeModelProjectClass.mat differ