Main Content

Detect and Replace Subsystem Clones Programmatically

Clones are modeling patterns that have identical block types and connections. You can refactor your model by creating library blocks from subsystem clones and replacing the clones with links to those library blocks, which enable you to reuse components. For more information about clones, see Enable Component Reuse by Using Clone Detection.

Programmatically checking for clones during the model submission process helps you identify opportunities to reuse subsystems before the model is deployed into the main product branch. When updating your model, you can use the Clone Detector app and clone detector API simultaneously. When you use the clone detector API, the detected clones appear in the Simulink Editor.

This example shows how to use the clone detection APIs to identify and replace clones in a single model by creating a library file with subsystem blocks and replacing the clones with links to blocks in the library file.

In this example, you learn how to use:

Identify Clones in a Model

  1. Open the model ex_clone_detection_B. At the MATLAB® command line, enter:

    addpath(fullfile(docroot,'toolbox','simulink','examples'))
    ex_clone_detection_B

    Save the model in the current working directory.

  2. To find subsystem clones, use the function Simulink.CloneDetection.findClones(). This function creates an object called cloneResults.

     cloneResults = Simulink.CloneDetection.findClones('ex_clone_detection_B')
    
    cloneResults = 
      Results with properties:
            Clones: [1×1 struct]
      ExceptionLog: ''

    The cloneResults object has Clones, which is a structure with two fields, Summary and CloneGroups, and ExceptionLog.

     cloneResults.Clones
    
    ans = 
      struct with fields:
            Summary: [1×1 struct]
        CloneGroups: [1×2 struct]
    
  3. View the Summary field.

     cloneResults.Clones.Summary
    
    ans = 
      struct with fields:
                     CloneGroups: 2
                   SimilarClones: 5
                     ExactClones: 0
                          Clones: 5
        PotentialReusePercentage: [1×1 struct]
    

    In this example, the model has two CloneGroups with matching subsystem patterns, five SimilarClones, and zero ExactClones, and the five subsystem Clones.

  4. View the CloneGroups field.

     cloneResults.Clones.CloneGroups
    
    ans = 
      1×2 struct array with fields:
        Name
        Summary
        CloneList
    

    The model in this example returns an array of two CloneGroups. Each array includes the Name, Summary and CloneList.

  5. View the details of first clone group.

     cloneResults.Clones.CloneGroups(1)
    
    ans = 
      struct with fields:
             Name: 'Similar Clone Group 1'
          Summary: [1×1 struct]
        CloneList: {3×1 cell}
    
  6. View the Summary.

     cloneResults.Clones.CloneGroups(1).Summary
    
    ans = 
      struct with fields:
        ParameterDifferences: [1×1 struct]
                      Clones: 3
              BlocksPerClone: 8
                   CloneType: 'Similar'
             BlockDifference: 1
    
  7. View the CloneList of the first CloneGroup.

     cloneResults.Clones.CloneGroups(1).CloneList
    
    ans =
      3×1 cell array
        {'ex_clone_detection_B/Subsystem1'}
        {'ex_clone_detection_B/Subsystem2'}
        {'ex_clone_detection_B/Subsystem3'}
    

    Similarly, You can find the results of other CloneGroups using the above steps.

Replace Clones in a Model

  1. To replace clones in a model, use the function Simulink.CloneDetection.replaceClones(). This function uses the cloneResults object from the findClones function.

     cloneReplacementResults = Simulink.CloneDetection.replaceClones(cloneResults)
    
    cloneReplacementResults = 
      ReplacementResults with properties:
        ReplacedClones: [1×5 struct]
        ExcludedClones: {}
    

    The cloneReplacementResults object includes two properties, ReplacedClones and ExcludedClones.

  2. View the contents of ReplacedClones property.

    cloneReplacementResults.ReplacedClones
    
    ans = 
      1×5 struct array with fields:
        Name
        ReferenceSubsystem
    

    The 1-by-5 array indicates that the function replaced five subsystem clones in the model.

  3. View the list of replaced subsystem clones.

     struct2table(cloneReplacementResults.ReplacedClones)
    
    ans =
      5×2 table
                       Name                         ReferenceSubsystem      
        ___________________________________    _____________________________
        {'ex_clone_detection_B/Subsystem1'}    {'newLibraryFile/Subsystem1'}
        {'ex_clone_detection_B/Subsystem2'}    {'newLibraryFile/Subsystem1'}
        {'ex_clone_detection_B/Subsystem3'}    {'newLibraryFile/Subsystem1'}
        {'ex_clone_detection_B/SS3'       }    {'newLibraryFile/SS1'       }
        {'ex_clone_detection_B/SS4'       }    {'newLibraryFile/SS1'       }
    

Identify Clones Using Subsystem Reference Blocks

  1. Save the model and library file in the current working directory.

    ex_clone_detection_E
    libname
  2. Use the Simulink.CloneDetection.Settings() class to create an object that specifies certain conditions for finding clones in a model.

     cloneDetectionSettings = Simulink.CloneDetection.Settings()
    
    cloneDetectionSettings = 
    
      Settings with properties:                            
                                IgnoreSignalName: 0
                             IgnoreBlockProperty: 0
                          ExcludeModelReferences: 0
                             ExcludeLibraryLinks: 0
                          ExcludeInactiveRegions: 0
                          SelectedSystemBoundary: ''
                         DetectClonesAcrossModel: 0
                  FindClonesRecursivelyInFolders: 1
                        ParamDifferenceThreshold: 50
        ReplaceExactClonesWithSubsystemReference: 0
                                       Libraries: {}
                                         Folders: {}
  3. Set the ParamDifferenceThreshold parameter. This parameter specifies the number of differences that subsystems must have to be considered clones.

     cloneDetectionSettings.ParamDifferenceThreshold = 0
    
    cloneDetectionSettings = 
    
      Settings with properties:                            
                                IgnoreSignalName: 0
                             IgnoreBlockProperty: 0
                          ExcludeModelReferences: 0
                             ExcludeLibraryLinks: 0
                          ExcludeInactiveRegions: 0
                          SelectedSystemBoundary: ''
                         DetectClonesAcrossModel: 0
                  FindClonesRecursivelyInFolders: 1
                        ParamDifferenceThreshold: 0
        ReplaceExactClonesWithSubsystemReference: 0
                                       Libraries: {}
                                         Folders: {}

    A value of 0 indicates the subsystems must be identical.

  4. Add a reference library file to use to match the clone patterns in the cloneDetectionSettings object. In this example, SSL1 and SSL2 are subsystem patterns in the library libName.

    cloneDetectionSettings = cloneDetectionSettings.addLibraries('libname')
    
    cloneDetectionSettings = 
    
      Settings with properties:                            
                                IgnoreSignalName: 1
                             IgnoreBlockProperty: 0
                          ExcludeModelReferences: 0
                             ExcludeLibraryLinks: 0
                          ExcludeInactiveRegions: 0
                          SelectedSystemBoundary: ''
                         DetectClonesAcrossModel: 0
                  FindClonesRecursivelyInFolders: 1
                        ParamDifferenceThreshold: 50
        ReplaceExactClonesWithSubsystemReference: 0
                                       Libraries: {'C:\Users\Examples\libname.slx'}
                                         Folders: {}
  5. To find clones, execute the function Simulink.CloneDetection.findClones() using the model name and cloneDetectionSettings object.

     cloneResults = Simulink.CloneDetection.findClones('ex_clone_detection_E', cloneDetectionSettings)
    
    cloneResults = 
      Results with properties:
        Clones: [1×1 struct]
    
     cloneResults.Clones.Summary
    
    ans = 
      struct with fields:
                     CloneGroups: 2
                   SimilarClones: 5
                     ExactClones: 0
                          Clones: 5
        PotentialReusePercentage: [1×1 struct]
    

    In this example, the model has two CloneGroups, five SimilarClones, zero ExactClones, and five subsystem Clones.

  6. View the details of first CloneGroup.

     cloneResults.Clones.CloneGroups(1)
    
    ans = 
      struct with fields:
             Name: 'libname/SSL1'
          Summary: [1×1 struct]
        CloneList: {3×1 cell}
    

Replace Clones with Conditions

  1. 1. To specify conditions for replaceClones function, create a handle using the Simulink.CloneDetection.ReplacementConfig() class:

     cloneReplacementConfig = Simulink.CloneDetection.ReplacementConfig()
    
    cloneReplacementConfig = 
      ReplacementConfig with properties:
        LibraryNameToAddSubsystemsTo: 'newLibraryFile'
                       IgnoredClones: {}
    
  2. Add subsystems to the IgnoredClones list. In this example, ignore Subsystem1 to avoid replacing it with a clone.

    cloneReplacementConfig.addCloneToIgnoreList('ex_clone_detection_E/Subsystem1')
    
    ans = 
      ReplacementConfig with properties:
        LibraryNameToAddSubsystemsTo: 'newLibraryFile'
                       IgnoredClones: {'ex_clone_detection_E/Subsystem1'}
    
  3. To replace clones, use the replaceClones function with cloneResults and cloneReplacementConfig as the input arguments.

    cloneReplacementResults = Simulink.CloneDetection.replaceClones(cloneResults, cloneReplacementConfig)
    
    cloneReplacementResults = 
      ReplacementResults with properties:
        ReplacedClones: [1×4 struct]
        ExcludedClones: [1×1 struct]
    
  4. View the ReplacedClones property.

    struct2table(cloneReplacementResults.ReplacedClones)
    
    ans =
      4×2 table
                       Name                    ReferenceSubsystem
        ___________________________________    __________________
        {'ex_clone_detection_E/SS3'       }     {'libname/SSL1'} 
        {'ex_clone_detection_E/SS4'       }     {'libname/SSL1'} 
        {'ex_clone_detection_E/Subsystem1'}     {'libname/SSL2'} 
        {'ex_clone_detection_E/Subsystem2'}     {'libname/SSL2'} 
    

    The SSL1 and SSL2 Reference Subsystem blocks from the reference library replaced the subsystem clones in the model.

  5. View the ExcludedClones property.

    struct2table(cloneReplacementResults.ExcludedClones)
    
    ans =
      1×2 table
                       Name                    ReferenceSubsystem
        ___________________________________    __________________
        {'ex_clone_detection_E/Subsystem1'}     {'unselected'} 
    

Check the Equivalency of the Model

You can check if the updated model is equivalent with the original model by using the Simulink.CloneDetection.checkEquivalency() function. This function uses Simulink Test Manager to compare the simulation results of the saved original model with the updated model and saves the results in the checkEquiResults handle.

 checkEquiResults = Simulink.CloneDetection.checkEquivalency(cloneReplacementResults)
[21-Dec-2020 16:35:13] Running simulations...
[21-Dec-2020 16:35:32] Completed 1 of 2 simulation runs
[21-Dec-2020 16:35:33] Completed 2 of 2 simulation runs

checkEquiResults = 
  EquivalencyCheckResults with properties:
    List: [1×1 struct]

View the check equivalence results.

 checkEquiResults.List
ans = 
  struct with fields:
    IsEquivalencyCheckPassed: 1
               OriginalModel: 'm2m_ex_clone_detection_E/snapshot_2020_12_21_16_35_06_ex_clone_detection_E.slx'
                UpdatedModel: 'ex_clone_detection_E.slx'

The property IsEquivalencyCheckPassed is 1, which suggests that the models are equivalent. The OriginalModel and UpdatedModel properties show which models the function checked.

Related Topics