Main Content

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.

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.

For more information on analysis functions and architecture instances, see Analyze Architecture.

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 Profiles and Stereotypes.

In this example, the analysis function systemWithProps_1calculates the total cost of all components in the model and is compatible with the Analysis Viewer tool.

function systemWithProps_1(instance,varargin)

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.

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.

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.

See Also

| | | | | | | | | | |

Related Topics