Analysis Function Constructs
Analyze architectures to choose between design alternatives or improve existing designs. You can use analysis functions with System Composer™ architecture models to perform systems analysis and trade studies.
An analysis function is a MATLAB® function that computes values necessary to evaluate the architecture using the properties of each element in the model instance and instance-specific parameters on the component and architecture level.
Use an analysis function to calculate the result of an analysis and determine the optimal parameters to use for behavior models to simulate the architectural system.
Type | Section |
---|---|
Roll-up analysis | Roll-Up Analysis for Quadcopter Design |
Class-based analysis | Class-Based Analysis for Battery Sizing |
Allocation-based analysis | Allocation-Based Analysis for Tire Pressure Monitoring |
Remaining useful life (RUL) analysis | Remaining Useful Life Analysis for Mobile Robot Design |
Variant analysis | Variant Analysis for Insulin Infusion Pump Design |
Analysis with parameters | Analysis with Parameters for Average Wear of Tires in Automobile |
For more information on analysis functions and architecture instances, see Analyze Architecture.
Tip
To learn more about how System Composer concepts apply to systems engineering design, see System Composer Concepts.
Roll-Up Analysis for Quadcopter Design
Use a roll-up analysis function to calculate a total or average of model element property values. Assign properties to model elements using stereotypes. For more information, see Define and Style Stereotypes in Profiles.
In this example, the analysis function CostAndWeightRollupAnalysis
calculates the total cost of all components in the model and is compatible with the Analysis Viewer tool.
function CostAndWeightRollupAnalysis(instance,varargin) % Analysis function for the RobotPhysicalArchitecture.slx example % Calculate total price if instance.isComponent() && ~isempty(instance.Components)... && instance.hasValue('SystemProfile.PhysicalElement.UnitCost') sysComponent_unitPrice = 0; for child = instance.Components if child.hasValue('SystemProfile.PhysicalElement.UnitCost') comp_price = child.getValue('SystemProfile.PhysicalElement.UnitCost'); sysComponent_unitPrice = sysComponent_unitPrice + comp_price; end end instance.setValue('SystemProfile.PhysicalElement.UnitCost',sysComponent_unitPrice); end
This analysis function iterates through an architecture instance. First, the sysComponent_unitPrice
variable is set to zero so that every time the analysis is run, sums do not accumulate indefinitely. Each component instance is checked for a UnitCost
property value. All UnitCost
property values are summed up and saved in the sysComponent_unitPrice
variable. Finally, the UnitCost
property of the current component instance is updated with the value of sysComponent_unitPrice
. For more information, see Write Analysis Function and Simple Roll-Up Analysis Using Robot System with Properties.
In this example, a section of the analysis function calculateEndurance
calculates endurance for a quadcopter using component instance properties. The calculated endurance value is then set for the architecture instance of the quadcopter with the setValue
function.
if payloadBatteryCapacity == 0 totalPower = powerConsumption + hoverPower/efficiency; endurance = (batteryCapacity/1000)/(totalPower/voltage)*60; else payloadEndurance = (payloadBatteryCapacity/1000)/(powerConsumption/voltage)*60; flightEndurance = (batteryCapacity/1000)/((hoverPower/efficiency)/voltage)*60; if flightEndurance < payloadEndurance endurance = flightEndurance; else endurance = payloadEndurance; warning('Endurance is limited by payload electronics.') end end instance.setValue('AirVehicle.Endurance',endurance)
For more information and for the supporting files, see Calculate Endurance Using Quadcopter Architectural Design.
Class-Based Analysis for Battery Sizing
Use MATLAB classes for an analysis function to iterate over an object, or instantiation of the class.
In this example, the class called computeBatterySizing
involves properties and methods useful for the analysis function computeLoad
.
classdef computeBatterySizing < handle properties totalCrankingInrushCurrent; totalCrankingCurrent; totalAccesoriesCurrent; totalKeyOffLoad; batteryCCA; batteryCapacity; puekertcoefficient; end methods function obj = computeBatterySizing(obj) obj.totalCrankingInrushCurrent = 0; obj.totalCrankingCurrent = 0; obj.totalAccesoriesCurrent = 0; obj.totalKeyOffLoad = 0; obj.batteryCCA = 0; obj.batteryCapacity = 0; obj.puekertcoefficient = 1.2; end function obj = displayResults(obj) tempNumdaysToDischarge = ... (((obj.batteryCapacity/obj.puekertcoefficient)*0.3)/(obj.totalKeyOffLoad*1e-3))/24; disp("Total KeyOffLoad: " + num2str(obj.totalKeyOffLoad) + " mA"); disp("Number of days required for KeyOffLoad to discharge 30% of battery: " + ... num2str(tempNumdaysToDischarge) + "."); disp("Total CrankingInRush current: " + num2str(obj.totalCrankingInrushCurrent) + " A"); disp("Total Cranking current: " + num2str(obj.totalCrankingCurrent) + " A"); if(obj.totalCrankingCurrent > obj.batteryCCA) disp("The Cold Cranking Amps of the specified battery is not sufficient to start the car 0 F.") else disp("CCA of the specified battery is sufficient to start the car at 0 F.") end end end end
For more information and for the supporting files, see Battery Sizing and Automotive Electrical System Analysis.
Allocation-Based Analysis for Tire Pressure Monitoring
A functional-to-logical allocation matrix allocates components in a functional architecture to components in a logical architecture. Coverage analysis is the most basic form of analysis to determine whether all elements have been allocated.
First, open the project for this example. Then, load the allocation set and collect the scenarios.
scExampleTirePressureMonitorSystem
allocSet = systemcomposer.allocation.load('FunctionalAllocation');
scenario = allocSet.Scenarios;
Verify that each function in the system is allocated.
import systemcomposer.query.*; [~, allFunctions] = ... allocSet.SourceModel.find(HasStereotype(IsStereotypeDerivedFrom("TPMSProfile.Function"))); unAllocatedFunctions = []; for i = 1:numel(allFunctions) if isempty(scenario.getAllocatedTo(allFunctions(i))) unAllocatedFunctions = [unAllocatedFunctions allFunctions(i)]; end end if isempty(unAllocatedFunctions) fprintf('All functions are allocated'); else fprintf('%d Functions have not been allocated', numel(unAllocatedFunctions)); end
All functions are allocated
The output verifies that all functions are allocated.
For more information and for the supporting files, see Allocate Architectures in Tire Pressure Monitoring System.
Remaining Useful Life Analysis for Mobile Robot Design
Remaining useful life (RUL) analysis estimates the time remaining before different subsystems fail. The goal is to anticipate maintenance and thus minimize system disruptions.
In this example, the analysis function scMobileRobotAnalysis
is compatible with the Analysis Viewer tool.
function scMobileRobotAnalysis(instance,varargin) ExpectedYearsBeforeFirstMaintenance = 2; if ~instance.isArchitecture() if instance.hasValue("HardwareBaseStereotype.Life") Life = instance.getValue("HardwareBaseStereotype.Life"); UsagePerDay = instance.getValue("HardwareBaseStereotype.UsagePerDay"); UsagePerYear = instance.getValue("HardwareBaseStereotype.UsagePerYear"); WillSurvive = Life > UsagePerDay * UsagePerYear * ExpectedYearsBeforeFirstMaintenance; instance.setValue("HardwareBaseStereotype.ExceedExpectedMaintenance", WillSurvive); end end end
After running this analysis function, you can optimize the desired first expected maintenance time in years. Each component that exceeds the expected maintenance time, in this case set to two years, is flagged with a check box. Unchecked components should be optimized or replaced with longer-lasting parts.
For more information and for the supporting files, see Define Stereotypes and Perform Analysis for Mobile Robot.
Variant Analysis for Insulin Infusion Pump Design
Use variant analysis to choose one optimal combination of variants by comparing them with a calculated metric.
In this example, the analysis function OutcomeAnalysis
is used to determine the best configuration for an insulin infusion pump. This standalone analysis function does not involve the Analysis Viewer tool. Instead, the analysis function uses the iterate
function and can be executed directly from the MATLAB Command Window.
The OutcomeAnalysis
function first gathers all variant choice components named Pump
and BGSensor
.
function outcomes = OutcomeAnalysis() modelname = 'InsulinInfusionPumpSystem'; therapyModel = systemcomposer.openModel(modelname); components = therapyModel.Architecture.Components; for idx = 1:numel(components) if strcmp(components(idx).Name,'Pump') pumps = components(idx).getChoices; pumpNames = {}; for jdx = 1:numel(pumps) pumpNames{end+1} = pumps(jdx).Name; end elseif strcmp(components(idx).Name,'BGSensor') sensors = components(idx).getChoices; sensorNames = {}; for jdx = 1:numel(sensors) sensorNames{end+1} = sensors(jdx).Name; end end end
The analysis function then collects all variant combinations to iterate over.
config.Sensor = sensorNames{1}; config.Pump = pumpNames{1}; configs = {}; for idx = 1:numel(sensorNames) for jdx = 1:numel(pumpNames) config.Sensor = sensorNames{idx}; config.Pump = pumpNames{jdx}; configs{end+1} = config; end end
The analysis function activates the variants one by one, iterates over the model properties, and collects outcomes. To set variant combinations, OutcomeAnalysis
uses the setVariants
function. To compute the outcomes, OutcomeAnalysis
uses the computeOutcome
function.
outcomes = {}; for idx = 1:numel(configs) hOutcome = OutcomeContainer(configs{idx}); therapyModel.iterate('Topdown',@setVariants,configs{idx}); therapyModel.iterate('BottomUp',@computeOutcome,hOutcome); hOutcome.setWeights([1e-6 1 10 1 1000]'); outcomes{end+1} = hOutcome; end
Finally, the analysis function plots the net outcome to reveal the optimal design choice.
properties = {'Lower NRE','Higher Accuracy','Better Compliance',... 'Sooner To Market','Lower Operating Cost'}; plotMatrix = zeros(numel(outcomes), numel(properties)); plotStrings = {}; for idx = 1:numel(outcomes) plotStrings{idx} = [outcomes{idx}.Sensor '+' outcomes{idx}.Pump]; plotMatrix(idx,1) = 1/(outcomes{idx}.NRE); plotMatrix(idx,2) = outcomes{idx}.Accuracy; plotMatrix(idx,3) = outcomes{idx}.Compliance; plotMatrix(idx,4) = 1/(outcomes{idx}.TimeToMarket); plotMatrix(idx,5) = 1/(outcomes{idx}.AnnualCost); end colmin = zeros(1,5); colmax = max(plotMatrix); normalizedMatrix = rescale(plotMatrix,'InputMin',colmin,'InputMax',colmax); if exist('spider_plot') == 2 fig = figure; spider_plot(normalizedMatrix,'AxesLabels',properties,'FillOption','on',... 'FillTransparency',0.1,'AxesDisplay','one'); title(sprintf('Trade Study Outcome'),... 'FontSize', 14); legend(plotStrings, 'Location', 'eastoutside'); pos = fig.Position; pos(2) = pos(2) - pos(4); pos(3) = 2*pos(3); pos(4) = 2*pos(4); fig.Position = pos; else vals = sum(normalizedMatrix,2)/5; x_labels = categorical(plotStrings); h = bar(x_labels,vals); title('Net outcome'); ax = h.Parent; ax.YLabel.String = 'Normalized units'; end
For more information and for the supporting files, see Design Insulin Infusion Pump Using Model-Based Systems Engineering.
Analysis with Parameters for Average Wear of Tires in Automobile
Parameters in an architecture model provide a way to define instance-specific
parameter values. These parameter values are unique to different instances of the same
model referenced across multiple components. You can use a reference component of the
standard template for a wheel, and use that wheel four times in an automobile
architecture model, by linking the mWheel.slx
model to four different
components.
Use parameters in analysis to analyze the Wear
parameter value
across four wheels and calculate the average wear of the tires as a measure of
durability.
function calculateAverages(instance,varargin) % Analysis function calculating the average wear of tires in a vehicle % Pass the number of tires as an input to the analysis function if numel(varargin) > 0 numTires = eval(varargin{1}); end % Get the architecture instance as a place to accumulate the total wear using a % stereotype property if instance.isArchitecture archInst = instance; else % Get the architecture instance from the path of the node currently being % iterated instName = strsplit(instance.QualifiedName,'/'); archInst = systemcomposer.analysis.lookup(string(instName{1})); end if instance.isComponent && any(instance.getParameterNames.matches("Wear")) % Get the stored stereotype property value on the architecture instance accumulatedValue = archInst.getValue('AxleProfile.Metrics.total_wear'); % Get the evaluated parameter value from the instance currentValue = instance.getEvaluatedParameterValue("Wear"); % Update the accumulated value newValue = accumulatedValue + currentValue; % Update the stereotype property value on the architecture instance archInst.setValue('AxleProfile.Metrics.total_wear',num2str(newValue)); if newValue > 0 fprintf(sprintf("Average wear of tires: %f\n", ... newValue/numTires)); end end end
For more information and for the supporting files, see Analysis for Average Wear of Tires in Automobile.
See Also
systemcomposer.analysis.Instance
| iterate
| instantiate
| deleteInstance
| update
| refresh
| save
| loadInstance
| lookup
| getValue
| setValue
| hasValue
| getParameter
| getEvaluatedParameterValue
| getParameterNames
| getParameterValue
| setParameterValue