Quantcast

MATLAB Coder

Using Dynamic Memory Allocation for an "Atoms" Simulation

This example shows how to generate code for a MATLAB algorithm that runs a simulation of bouncing "atoms" and returns the result after a number of iterations. There are no upper bounds on the number of atoms that the algorithm accepts, so this example takes advantage of dynamic memory allocation.

Prerequisites

There are no prerequisites for this example.

Create a New Folder and Copy Relevant Files

The following code will create a folder in your current working folder (pwd). The new folder will contain only the files that are relevant for this example. If you do not want to affect the current folder (or if you cannot generate files in this folder), change your working folder.

Run Command: Create a New Folder and Copy Relevant Files

coderdemo_setup('coderdemo_atoms');

About the 'run_atoms' Function

The run_atoms.mrun_atoms.m function runs a simulation of bouncing atoms (also applying gravity and energy loss).

help run_atoms
  atoms = run_atoms(atoms,n)
  atoms = run_atoms(atoms,n,iter)
  Where 'atoms' the initial and final state of atoms (can be empty)
        'n' is the number of atoms to simulate.
        'iter' is the number of iterations for the simulation
           (if omitted it is defaulted to 3000 iterations.)

Set Up Code Generation Options

Create a code generation configuration object

cfg = coder.config;
% Enable dynamic memory allocation for variable size matrices.
cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';

Set Up Example Inputs

Create a template structure 'Atom' to provide the compiler with the necessary information about input parameter types. An atom is a structure with four fields (x,y,vx,vy) specifying position and velocity in Cartesian coordinates.

atom = struct('x', 0, 'y', 0, 'vx', 0, 'vy', 0);

Generate a MEX Function for Testing

Use the command 'codegen' with the following arguments:

'-args {coder.typeof(atom, [1 Inf]),0,0}' indicates that the first argument is a row vector of atoms where the number of columns is potentially infinite. The second and third arguments are scalar double values.

'-config cfg' enables dynamic memory allocation, defined by workspace variable cfg

codegen run_atoms -args {coder.typeof(atom, [1 Inf]),0,0} -config cfg -o run_atoms_mex

Run the MEX Function

The MEX function simulates 10000 atoms in approximately 1000 iteration steps given an empty list of atoms. The return value is the state of all the atoms after simulation is complete.

atoms = run_atoms_mex([],10000,1000)
Iteration: 50
Iteration: 100
Iteration: 150
Iteration: 200
Iteration: 250
Iteration: 300
Iteration: 350
Iteration: 400
Iteration: 450
Iteration: 500
Iteration: 550
Iteration: 600
Iteration: 650
Iteration: 700
Iteration: 750
Iteration: 800
Iteration: 850
Iteration: 900
Iteration: 950
Iteration: 1000
Completed iterations: 1000

atoms = 

1x10000 struct array with fields:

    x
    y
    vx
    vy

Run the MEX Function Again

Continue the simulation with another 500 iteration steps

atoms = run_atoms_mex(atoms,10000,500)
Iteration: 50
Iteration: 100
Iteration: 150
Iteration: 200
Iteration: 250
Iteration: 300
Iteration: 350
Iteration: 400
Iteration: 450
Iteration: 500
Completed iterations: 500

atoms = 

1x10000 struct array with fields:

    x
    y
    vx
    vy

Generate a Standalone C Code Library

To generate a C library, create a standard configuration object for libraries:

cfg = coder.config('lib');

Enable dynamic memory allocation

cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';

In MATLAB the default data type is double. However, integers are usually used in C code, so pass int32 integer example values to represent the number of atoms and iterations.

codegen run_atoms -args {coder.typeof(atom, [1 Inf]),int32(0),int32(0)} -config cfg

Inspect Generated Code

When creating a library the code is generated in the folder codegen/lib/run_atoms/ The code in this folder is self contained. To interface with the compiled C code you need only the generated header files and the library file.

dir codegen/lib/run_atoms
.                       rt_nonfinite.h          run_atoms_emxutil.h     
..                      rt_nonfinite.o          run_atoms_emxutil.o     
buildInfo.mat           rtw_proj.tmw            run_atoms_initialize.c  
codeInfo.mat            rtwtypes.h              run_atoms_initialize.h  
interface               run_atoms.a             run_atoms_initialize.o  
rtGetInf.c              run_atoms.c             run_atoms_ref.rsp       
rtGetInf.h              run_atoms.h             run_atoms_rtw.mk        
rtGetInf.o              run_atoms.o             run_atoms_terminate.c   
rtGetNaN.c              run_atoms_emxAPI.c      run_atoms_terminate.h   
rtGetNaN.h              run_atoms_emxAPI.h      run_atoms_terminate.o   
rtGetNaN.o              run_atoms_emxAPI.o      run_atoms_types.h       
rt_nonfinite.c          run_atoms_emxutil.c     

Write a C Main Function

Typically, the main function is platform-dependent code that performs rendering or some other processing. In this example, a pure ANSI-C function produces a file 'run_atoms_state.m' which (when run) contains the final state of the atom simulation.

type run_atoms_main.c
/* Include standard C libraries */
#include <stdio.h>

/* The interface to the main function we compiled. */
#include "codegen/lib/run_atoms/run_atoms.h"

/* The interface to EMX data structures. */
#include "codegen/lib/run_atoms/run_atoms_emxAPI.h"

int main(int argc, char **argv)
{
    int i;
    emxArray_Atom *atoms;
 
    /* Main arguments unused */
    (void) argc;
    (void) argv;
    
    /* Initially create an empty row vector of atoms (1 row, 0 columns) */
    atoms = emxCreate_Atom(1, 0);
    
    /* Call the function to simulate 10000 atoms in 1000 iteration steps */
    run_atoms(atoms, 10000, 1000);
    
    /* Call the function again to do another 500 iteration steps */
    run_atoms(atoms, 10000, 500);
    
    /* Print the result to standard output */
    for (i = 0; i < atoms->size[1]; i++) {
        printf("%f %f %f %f\n",
            atoms->data[i].x, atoms->data[i].y, atoms->data[i].vx, atoms->data[i].vy);
    }
    
    /* Free memory */
    emxDestroyArray_Atom(atoms);
    return(0);
}

Create a Configuration Object for Executables

cfg = coder.config('exe');
cfg.DynamicMemoryAllocation = 'AllVariableSizeArrays';

Generate a Standalone Executable

You must pass the function (run_atoms.m) as well as custom C code (run_atoms_main.c) The 'codegen' command automatically generates C code from the MATLAB code, then calls the C compiler to bundle this generated code with the custom C code (run_atoms_main.c).

codegen run_atoms run_atoms_main.c -args {coder.typeof(atom, [1 Inf]),int32(0),int32(0)} -config cfg

Run the Executable

After simulation is complete, this produces the file 'atoms_state.mat'. The MAT file is a 10000x4 matrix, where each row is the position and velocity of an atom (x, y, vx, vy) representing the current state of the whole system.

[~,atoms_data] = system(['.' filesep 'run_atoms']);
fh = fopen('atoms_state.mat', 'w');
fprintf(fh, '%s', atoms_data);
fclose(fh);

Fetch the State

Running the executable produced 'atoms_state.mat'. Now, recreate the structure array from the saved matrix

load atoms_state.mat -ascii
clear atoms
for i = 1:size(atoms_state,1)
    atoms(1,i).x  = atoms_state(i,1);
    atoms(1,i).y  = atoms_state(i,2);
    atoms(1,i).vx = atoms_state(i,3);
    atoms(1,i).vy = atoms_state(i,4);
end

Render the State

Call 'run_atoms_mex' with zero iterations to render only

run_atoms_mex(atoms, 10000, 0);

Clean Up

Remove files and return to original folder

Run Command: Cleanup

if ispc
    delete run_atoms.exe
else
    delete run_atoms
end
delete atoms_state.mat
cleanup