7.17 Variable names and programming rules (syntax)#
The following rules are used in programming SDT and OpenFEM as it makes reading the source code easier.
All SDT functions are segmented and tagged so that the function structure is clearly identified. Its tree structure can be displayed and browsable through the sdtweb _taglist interface. You should produce code compatible with this browser including tags (string beginning by # in a comment), in particular at each command of your function.
In addition, input parsing section 7.17.4 section 7.17.5 and some utilities for directory handling section 7.17.6, post-treatment display section 7.17.6 and figure formatting/capturing section 7.17.6 have been standardized.
7.17.1 Variable naming conventions#
Standardized variable names are
carg | index of current argument. For functions with variable number of inputs, one seeks the next argument with NewArg=varargin{carg};carg=carg+1; |
CAM, Cam | string command to be interpreted. Cam is the lower case version of CAM. Input parsing conventions are described in ParamEdit and urnPar. |
j1,j2,j3 ... | loop indices. |
jGroup,jElt,jW | indices for element groups, elements, integration points. For code samples use help('getegroup') |
jPar | indices for experiments,see fe_range. |
i,j | unit imaginary √−1. i,j should never be used as indices to avoid any problem overloading their default value. |
i1,i2,i3 ... | integer values intermediate variables |
r1,r2,r3 ... | real valued variables or structures |
ind,in2,in3 ... | vectors of indices, cind is used to store the complement of ind when applicable. |
out,out1,out2 ... | output variables. |
The following names are also used throughout the toolbox functions
model, mo1, mo2 ... | SDT model structures. |
node,FEnode, n1, n2 ... | nodes, FEnode is reserved as a global variable. |
elt, FEelt, el1, el2 ... | elements, FEelt is reserved as a global variable. |
EGroup, nGroup | starting index of each group and number of groups in an element structure, see help('getegroup'). |
cEGI | index of elements for a given group in an element structure, see help('getegroup'). |
NNode | reindexing vector, verifies NodeInd=NNode(NodeId). Can be built using NNode=sparse(node(:,1),1,1:size(node,1)). |
nd | reindexing object for DOF, verifies DofPos=feval(nd.getPosFcn,nd,DOF). Is built using nd=feval(fe_mknl('@getPosFromNd'),[],DOF); |
RunOpt | run options, a structure used to store options that are used in a command. RO can also be used. |
adof | current active DOF vector. |
cf | pointer to a feplot figure. |
gf, uf, ga, ua, go, uo | respectively handle and userdata to a figure, handle and userdata to an axis, handle and userdata to a graphics subobject. |
gc, evt | respectively active object and associated event in Java triggered callbacks. |
7.17.2 GetData: model input parsing and dereference object copies#
SDT uses objects to store multiple types of data. It is however sometimes required to generate a local derefrenced copy. The main example is to generate a model variant from the one stored in feplot without impacting the latter one. To avoid multiple object testing in the code, method sdth.GetData performs an efficient generic recovery if needed.
The following calls are supported
- Generic call: r2=sdth.GetData(r1). If r1 is an object and has a GetData method,, the method will output the result of r1.GetData, otherwise, the output will be equal to the input.
- Class restrictions: r2=sdth.GetData(r1,'class1',...); will perform the generic call but will only apply GetData method if input r1 is of a class specified in the list. The class list supports wildcards for class extensions, subtypes of v_handle and vhandle.matrix are supported. v_handle.so, vhandle.matrix.mklst, vhandle.matrix.mkls* are thus supported. v_handle and vhandle.matrix types are treated as equivalent and will thus be indifferently be tested.
- -mdl call: [mo1,cf,model]=sdth.GetData(cf,'-mdl') is designed to recover model objects. Input cf can be either a FeplotFig sdth object, or a model v_handle, or an nmap, or a standard structure. Output mo1 is a local dereferenced copy similar to cf.mdl.GetData, or model.GetData depending on the case. Output cf is the sdth.FeplotFig object if the input was an object, empty otherwise. Ouput model is the v_handle model, similar to cf.mdl is the input is an object, otherwise is is equal to the entry and output mo1. Models now being possibly stored in nmap, the method is compatible for nmap objects or structures with an .nmap field when a .Elt field is not present. In such case, the second output will be the input copy so as to be coherent with the input object.
- -rec recursive call: r2=sdth.GetData(r1,'-rec') will perform the same call than the generic one, but recursively until GetData call does not change the ouput. This call is compatible with class restrictions, the list to be provided avec the -rec token.
- -warndel warning call for deleted handles: will output deleted handle string instead of an empty copy if the handle containing the object source has been deleted.
Sample calls are thus the following
cf=feplot; model=cf.mdl;
% Generic recovery for all objects mo1=sdth.GetData(model)
% Specific model recovery [mo1,cg,mo2]=sdth.GetData(cf,'-mdl'); [mo1,cg,mo2]=sdth.GetData(cf.mdl,'-mdl'); [mo1,cg,mo2]=sdth.GetData(cf.mdl.GetData,'-mdl');
% Model recovery from nmap % CurModel storage model=struct('Node',[1 0 0 0 0 0 0],'Elt',feutil('addelt',[],'mass1',1)); RT=struct('nmap',vhandle.nmap); RT.nmap('CurModel')=model;
% recover model [mo1,RT]=sdth.GetData(RT,'-mdl'); % direct alternative from nmap [mo1,nmap]=sdth.GetData(RT.nmap,'-mdl');
% Restraint to specific classes r1=pmat(ones(1)); r2=sdth.GetData(r1); % generic call r3=sdth.GetData(r1,'pmat'); % restrict to pmat r3=sdth.GetData(r1,'pmat','omat'); % allow pmat or omat r3=sdth.GetData(r1,'omat'); % restrict on omat: no effect on pmat
7.17.3 Coding style#
The coding styles convention are detailed in the example below.
-
Tags for taglist are marked with the # token, not to interfere with pragma tokens, ensure that it is not directly following a %
, but leave at least one space.
- The tag level can be specified by placing -i at the end of the line, i being the level. If not each tag is assumed to be level 1. Tags with lines finishing by - - - or after the #SubFunc tag are assumed level 2.
- By default, the taglist will concatenate consecutive tags with the same starting letters, the subsequent tags will thus be shifted.
- Code sections are usually delimited using the cell display %% .
- The first input argument should be a string whose parsing will determine the command to execute and associated command options.
- An error should be returned if the command is unknown.
- Access from the outside to subfunction handles should be made possible through a call suf=my_func('@my_sub_fun').
- Subversion tags should be present to allow easy administration using cvs or svn, in a unique command cvs, that will output a string containing the cvs or svn tags.
function [out,out1,out2,out3]=my_func(varargin);% Here you should place your help % SDT functions always use varargin for input and [out,out1, ...] for % output.
% ask MATLAB to avoid some warnings the in the editor MLint %#ok<*NASGU,*ASGLU,*CTCH,*TRYNC,*NOSEM>
% Get the command in varargin{1} and strip front/end blanks with comstr % CAM is as input, Cam is lower case. if nargin<1; CAM=''; Cam='';carg=1; else;[CAM,Cam]=comstr(varargin{1},1); carg=2; end
%% #Top : main level command Top ------------------------------ % the %% is to use Matlab cell, while #Top is a sdtweb _taglist tag % by default tags are set to level 1 % Now test that Cam starts by 'top' and then strip 3 characters and trim (+1) if comstr(Cam,'top');[CAM,Cam]=comstr(CAM,4);
if comstr(Cam,'manual');[CAM,Cam]=comstr(CAM,7); %% #TopLevel2 : subcommand level 2 - - - - - - - - - -2 % - - - tells sdtweb this is a level 2 tag % ending the line with -2 is sufficient in practice % any other level can be used by adding a higher number at the end of the tag line
% recover other inputs r1=varargin{carg}; carg=carg+1; % get input and increment counter % get optionnal input arguments if carg<=nargin; r2=carargin{carg}; carg=carg+1; else; r2=[]; end % For more complex input arguments with many run options, % use instead the input parsing conventions (see sdtweb syntax#syntInp)
%% #TopEnd -2 else; error('Top%s unknown',CAM); end %% #End : typical commands placed at end of function elseif comstr(Cam,'@');out=eval(CAM); elseif comstr(Cam,'cvs') out='$Revision: 1.33 $ $Date: 2024/06/22 16:43:02 $'; else; error('my_func %s unknown',CAM); end
%% #SubFunc : indicates start of subfunctions to taglist parsing %% #my_sub_fun - - ------------------------------------------ function out=my_sub_fun(varargin)
7.17.4 Input parsing conventions, ParamEdit#
Passing command options is a critical feature to enable little behavior alteration as function of the user needs although most of the functionality is the same. This allows in particular limiting code duplication.
From the input CAM variable, command option parsing utilities have been defined and standardized. See also the alternate urnPar format and the CinCell structure which describes possible extensions. The goal is to build a run option structure from the input command string while keeping the possibility to provide it as an extra argument.
The command parsing code is then
% Usual run options handling % first possible to recover in extra input if carg>nargin||~isstruct(varargin{carg});RO=struct; else;RO=varargin{carg};carg=carg+1; end % Then parse CAM for command options, % and assign default values to unspecified options % values declared prior to the paramedit call are not overriden [RO,st,CAM]=cingui('paramedit -DoClean',[ ... 'param(val#%g#"Description")' ... 'token(#3#"token modes does...")' ... '-parS("string"#%s#"parS modes available...")' ... ],{RO,CAM}); Cam=lower(CAM); % If default parameters are more complex (arry, cell, ...) % Use the following syntax which add only missing fields % as default to the provided structure RO=sdth.sfield('AddMissing',RO,struct(... 'param2',linspace(1,100,3000),... % Default param2 vector 'param3',{{'fixdof','proid 1'}})); % Default param3 cell ...
The paramEdit call from cingui performs standard operations for each token in the second input string of the command. Each token follows the format token(val#fmt#"info"), and will generate a case sensitive field token in the structure RO. val is a default value that is applied if the field token is missing before the call. info is a string providing information on the token effect. fmt tells the type of input that should be parsed after the token, with the following rules:
- 3 Only checks for the presence of token in the command without any other value. Sets field token to 1(double) if found, 0(as double) if not. val must remain empty. e.g. Top token, will set RO.token=1.
- 31 Behaves as type 3 but also checks for an optional integer input. Sets field token to 1(double) if found, 0(as double) if not, or to the found integer if found. val must remain empty. e.g. Top token 2 will set RO.token=2, and Top token will set RO.token=1.
- %g Checks for token followed by a float. If found RO.token is set to the float, if no float is found the field is left empty. If the token is not found, the default value val is set. e.g. Top token 3.14 will set RO.token=3.14.
- %i Checks for token followed by an integer. If found RO.token is set to the integer, if no integer is found the field is left empty. If the token is not found, the default value val is set. e.g. Top token 31 will set RO.token=31.
- %s Checks for token followed by a string (delimited by two "). If found RO.token is set to the string, if no string is found the field is left empty. If the token is not found, the default value val is set. e.g. Top token"test" will set RO.token='test'. Note that for this type if val is not empty one defines the token as token("val"#%s#"info") , but if val is empty, one should use token(#%s#"info") .
Advanced usage may require nested string tokens, i.e. a string token that will itself contain a token list for a subcommand. The base parsing strategy is rather robust to these cases, but nested string matching patterns may sometimes be tricky. In such case, one should use nested double quotes for the inside string. E.g. 'command-token"subcommand -subtok' ' "subval" ' '"'. With such input, the subtoken will be correctly handled.
RO=struct; CAM='comm-tok"subcomm -subtok''"''subval''"''"'; [RO,st,CAM]=cingui('paramedit -DoClean',[ ... 'tok(#%s#"token value with nested string")' ... ],{RO,CAM}); Cam=lower(CAM); RO.tok % contains the full substring % then in the sub command [RB,st,CAM1]=cingui('paramedit -DoClean',[ ... 'subtok(#%s#" subtoken value with nested string")' ... ],{struct,RO.tok}); Cam1=lower(CAM1); RB.subtok % contains the proper value
The output CAM has been stripped from any parsed data.
The format -token(val#fmt#"info") will require the presence of -token in the command to generate the token field in RO.
By convention, to handle interference between the extra input argument RO and default values overriding, any field present in RO prior to calling paramEdit will be left unchanged by the command.
7.17.5 Input parsing conventions, urnPar#
Following the uniform resource name urn developments, commands are gradually changed from the ParamEdit to the parsing strategy implemented in sdtm.urnPar, where commands are of the form commandName{arg1,arg2}. See also the CinCell structure which describes possible extensions for GUI, ToolTip, ...
- arguments are found in a comma separated list between {}.
- the format string {mandatory%fmt}{optional%fmt} distinguishes between mandatory and optional arguments. The accepted formats are those described in ParamEdit.
- the command provides the mandatory parameters values directly val1,val2
- the command provides the optional parameters given parameter name followed directly by the value par1val1,par2val2
Example :
fmt='{freq%g,amplitude%g}{in%i,out%i,opt%31}'; CAM='CmdName{500,5.5,opt,in2}'; [CAM,RO]=sdtm.urnPar(CAM,fmt);
7.17.6 Commands associated to project application functions#
The development of project application functions follow some must have such as project directory handling section 7.17.6, post-treatment handling section 7.17.6, image capture generation section 7.17.6. Some of these steps have been standardized over the years, which are documented in the present sections.
wd,fname#
The files relative to a specific application are usually stored in a specific file tree. It is thus useful to access standard defined save directories in a robust manner, regardless of the operating system or the user. Standard applications developed by SDTools usually involve a user defined root directory from which the following subdirectories are defined
- m contains the project source code.
- tex contains the project documentation source code.
- mat contains reference data files.
- plots contains the image captures.
- doc contains other project support documentation.
Each of these directories may contain any further tree to class data as desired.
To allow efficient recovery of a specific subdirectory or file in the final project file architecture, sdtweb provides commands in its utilities (see sdtweb Utils) that should be used by the main project function to search the project architecture subdirectories.
The wd command should package a search in its known subdirectories.
%% #wd ------------------------------------------------- elseif comstr(Cam,'wd')if nargin==1 % output the possible root directories % assume this function is stored in root/m out=fileparts(which('my_func')); % possibly add specific root dirs outside the project % should be better handled with a preference wd2={'/p/my_files'}; % add as many as needed out=[out wd2];
else % get the subdirectory searched wd1=varargin{carg}; carg=carg+1; % get the project root directory (several ones admitted) wd0=my_func('wd'); % find the subdirectory out=sdtweb('_wd',wd0,wd1); end
The fname command should package a file search in the known subdirectories
%% #fname ----------------------------------------------- elseif comstr(Cam,'fname') fname=varargin{carg}; carg=carg+1; % get the available root directories wd=my_func('wd'); % search for the file out=sdtweb('_fname',fname,wd);
view#
The generation of displayed post-treatments should be handled by a command named View, that will centralize the feplot manipulations required to generate ad hoc displays. Variations of display are handled in the command, first and second input should be the feplot pointer and optionally a deformation data.
- Handling of legend (location, labels, ...) can be performed by defining a Legend field to deformation curves, see comgui def.Legend for more details.
- Handling of colorbars and their legends can be performed using fecom ColorBar and fecom ColorLegend commands.
- Stress post-treatments can be handled through a fe_caseg StressCut command.
- Energy post-treatment can be handled through fe_stress Ener and their corresponding display through fe_stress feplot
- Handling of color scales can be handled with fecom ColorScale.
A sample call to be handled by the view command could then be.
my_project('ViewUpStress',cf);
im#
The generation of image captures from figures (feplot iiplot or standard MATLAB figures) should be handled by a command named im, that will centralize formatting and saving. This command should
- Provide figure formatting data for implemented modes
- Perform figure formatting according to a required mode
- Perform figure capture and save to an appropriate directory
For details on figure formatting, see comgui objSet, for details on figure naming strategy see comgui ImFtitle, for low level image capturing calls, see comgui ImWrite.
A suggested layout for the im command of a sample my_func function is then
%% #im : figure formatting --------------------------------------------------- elseif comstr(Cam,'im') % sdt_table_generation('Rep{SmallWide}');comstr(ans,-30)if nargin==2 % generate the calling string pw0=pwd; if isfield(varargin{2},'ch') % multiple generation with imwrite ch RO=varargin{2};cf=feplot; % Create an possibly change to directory sdtkey('mkdircd',my_func('wd','plots',sscanf(cf.mdl.name,'%s',1))); RO.RelPath=1; % Save links with path relative to current position RO=iicom(cf,'imwrite',RO); fid=fopen('index.html','w');fprintf(fid,'%s',RO.Out{:});fclose(fid); cd(pw0);
elseif ~ischar(varargin{2}); % Apply reshaping to figure gf=varargin{2};if ~ishandle(gf);figure(gf);plot([0 1]);end cingui('objset',gf,my_func(CAM)) % if feplot, center the display if strcmpi(get(gf,'tag'),'feplot');iimouse('resetvie');end
elseif strcmpi(varargin{2},'.') % if '.' get automatic naming st=sprintf('imwrite-objSet"@my_func(''%s'')"-ftitle',varargin{1}); comgui(st);
else cd(my_func('wd','plots')); st=sprintf('imwrite-objSet"@my_func(''%s'')"-ftitle%s',varargin{1:2}); comgui(st); cd(pw0); end
elseif comstr(Cam,'imw1') % Figure formatting options for w1 out={'position',[NaN,NaN,450*[1.5 2]],'paperpositionmode','auto', ...
'@exclude',{'legend.*'},'@text',{'FontSize',14}, ... '@axes',{'FontSize',14,'box','on'}, ... '@ylabel',{'FontSize',14,'units','normalized'}, ... '@zlabel',{'FontSize',14,'units','normalized'}, ... '@title',{'FontSize',14}, ... '@line',{'linewidth',1}, ... '@xlabel',{'FontSize',14,'units','normalized'}};% elseif ... use as many commands as needed
else; error('%s unknown',CAM); end
This way, the following tasks can be easily performed
% Im calls for figure capturing gf=figure(1); plot([1 0]); % Capture an image from figure 1 with formatting w1 and named test.png my_func('imw1','test.png'); % Capture an image from figure 1 with formatting w1 with an automatic name my_func('imw1','.'); % Format figure 1 according to w1 options my_func('imw1',gf); % Get formatting options for w1 r1=my_func('imw1');
7.17.7 Commands associated to tutorials#
In a training function or in any function where a tutorial could be executed, the syntax is the following
elseif comstr(Cam,'tuto') %% #Tuto (implement standard behaviour of tuto command) -1 % Execute the tutorial with CAM commands or open the tuto tree if empty CAM eval(sdtweb('_tuto',struct('file','current_function_name','CAM',CAM))); if nargout==0; clear out; end elseif comstr(Cam,'tutoname') %% #TutoTutoname-2 % See sdtweb('LinkToHTML') % Open the HTML corresponding to the tutorial%% Step 1 : Description of step1 % See sdtweb('LinkToHTML') % Open HTML detailed doc related to this step
%% Step 1.1 : Description of substep 1.1
% Code to execute correponding to Step 1.1
%% Step 1.2 : Description of substep 1.1
% Code to execute correponding to Step 1.2
% Step 2 : Description of step2 % See sdtweb('LinkToHTML') % Open HTML detailed doc related to this step
% Code to execute correponding to Step 2
%% EndTuto
elseif comstr(Cam,'tutoname2') %% #TutoTutoname2-2 % See sdtweb('LinkToHTML') % Open the HTML corresponding to the tutorial
%% EndTuto
% elseif ... use as many commands as needed
This way, the following commands are usually executed :
% Open the tree containing all the tutorial and clickable buttons my_func('Tuto'); % Execute the whole tutorial (useful for test auto) my_func('TutoTutoname'); % Execute a tutorial up to a given step (here section 2.3) my_func('TutoTutoname -s2.3');