SDT-base Contents   Functions      PDF Index |
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.
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. |
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
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
The coding styles convention are detailed in the example below.
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)
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:
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.
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, ...
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);
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.
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
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);
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.
A sample call to be handled by the view command could then be.
my_project('ViewUpStress',cf);
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
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');
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');