/*****************************************************************************1*2* Elmer, A Finite Element Software for Multiphysical Problems3*4* Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland5*6* This library is free software; you can redistribute it and/or7* modify it under the terms of the GNU Lesser General Public8* License as published by the Free Software Foundation; either9* version 2.1 of the License, or (at your option) any later version.10*11* This library is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU14* Lesser General Public License for more details.15*16* You should have received a copy of the GNU Lesser General Public17* License along with this library (in file ../LGPL-2.1); if not, write18* to the Free Software Foundation, Inc., 51 Franklin Street,19* Fifth Floor, Boston, MA 02110-1301 USA20*21*****************************************************************************/2223/*******************************************************************************24*25* MATC main module.26*27*******************************************************************************28*29* Author: Juha Ruokolainen30*31* Address: CSC - IT Center for Science Ltd.32* Keilaranta 14, P.O. BOX 40533* 02101 Espoo, Finland34* Tel. +358 0 457 272335* Telefax: +358 0 457 230236* EMail: [email protected]37*38* Date: 30 May 199639*40* Modified by:41*42* Date of modification:43*44******************************************************************************/45/***********************************************************************46|47| MATC - Last Edited 9. 8. 198848|49***********************************************************************/5051/*======================================================================52|Syntax of the manual pages:53|54|FUNCTION NAME(...) params ...55|56$ usage of the function and type of the parameters57? explain the effects of the function58= return value and the type of value if not of type int59@ globals effected directly by this routine60! current known bugs or limitations61& functions called by this function62~ these functions may interest you as an alternative function or63| because they control this function somehow64^=====================================================================*/656667/*68* $Id: matc.c,v 1.7 2007/06/08 08:12:17 jpr Exp $69*70* $Log: matc.c,v $71* Revision 1.7 2007/06/08 08:12:17 jpr72* *** empty log message ***73*74* Revision 1.6 2006/02/07 10:21:42 jpr75* Changed visibility of some variables to local scope.76*77* Revision 1.5 2006/02/02 06:54:44 jpr78* small formatting changes.79*80* Revision 1.3 2005/08/25 13:44:22 vierinen81* windoze stuff82*83* Revision 1.2 2005/05/27 12:26:20 vierinen84* changed header install location85*86* Revision 1.1.1.1 2005/04/14 13:29:14 vierinen87* initial matc automake package88*89* Revision 1.2 1998/08/01 12:34:48 jpr90*91* Added Id, started Log.92*93*94*/9596#define MODULE_MATC97#include "elmer/matc.h"98#include "str.h"99#include "../config.h"100101#ifdef DEBUG102static FILE *fplog;103static int tot;104#pragma omp threadprivate (fplog, tot)105#endif106/*======================================================================107? main program, initialize few constants and go for it.108^=====================================================================*/109void mtc_init( FILE *input_file, FILE *output_file, FILE *error_file )110{111VARIABLE *ptr;112113char str[256];114115int i; /* i'm getting tired with all these i's */116117static char *evalHelp =118{119"eval( str )\n\n"120"Evaluate content variable. Another form of this command is @str.\n"121};122123static char *sourceHelp =124{125"source( name )\n\n"126"Execute commands from file given name.\n"127};128129static char *helpHelp =130{131"help or help(\"symbol\")\n\n"132"First form of the command gives list of available commands.\n"133"Second form gives help on specific routine.\n"134};135136#ifdef _OPENMP137/* Allocate listheaders for each thread separately */138#pragma omp parallel139{140/* Do malloc and initialize listheaders */141listheaders = (LIST *) malloc(sizeof(LIST)*MAX_HEADERS);142/* memory allocations */143listheaders[ALLOCATIONS].next = NULL;144listheaders[ALLOCATIONS].name = "Allocations";145/* global CONSTANTS */146listheaders[CONSTANTS].next = NULL;147listheaders[CONSTANTS].name = "Constants";148/* global VARIABLES */149listheaders[VARIABLES].next = NULL;150listheaders[VARIABLES].name = "Currently defined VARIABLES";151/* internal commands */152listheaders[COMMANDS].next = NULL;153listheaders[COMMANDS].name = "Builtin Functions";154/* user defined functions */155listheaders[FUNCTIONS].next = NULL;156listheaders[FUNCTIONS].name = "User Functions";157}158#endif /* _OPENMP */159160#ifdef DEBUG161fplog = fopen("matcdbg","w");162#endif163ALLOC_HEAD = (LIST *)NULL;164165/*166* input & output & error streams167*/168math_in = input_file;169math_err = error_file;170math_out = output_file;171172mtr_com_init(); /* initialize matrix handling commands */173var_com_init(); /* "" VARIABLE "" "" */174fnc_com_init(); /* "" function handling commands */175fil_com_init(); /* "" file handling commands */176gra_com_init(); /* "" graphics commands */177str_com_init(); /* "" string handling */178179/*180* and few others.181*/182com_init( "eval" , FALSE, FALSE, com_apply, 1, 1, evalHelp );183com_init( "source" , FALSE, FALSE, com_source, 1, 1, sourceHelp );184com_init( "help" , FALSE, FALSE, com_help , 0, 1, helpHelp );185com_init( "quit" , FALSE, FALSE, com_quit , 0, 0, "quit\n" );186com_init( "exit" , FALSE, FALSE, com_quit , 0, 0, "exit\n" );187188/*189* these constants will always be there for you.190*/191ptr = const_new("true", TYPE_DOUBLE, 1, 1);192M(ptr,0,0) = 1.0;193194ptr = const_new("false", TYPE_DOUBLE, 1, 1);195M(ptr,0,0) = 0.0;196197ptr = const_new("stdin", TYPE_DOUBLE, 1, 1);198M(ptr,0,0) = 0;199200ptr = const_new("stdout", TYPE_DOUBLE, 1, 1);201M(ptr,0,0) = 1;202203ptr = const_new("stderr", TYPE_DOUBLE, 1, 1);204M(ptr,0,0) = 2;205206ptr = const_new("pi", TYPE_DOUBLE, 1, 1);207M(ptr,0,0) = 2*acos(0.0);208209#if 0210/*211* trap INTERRUPT and Floating Point Exception signals212*/213signal(SIGFPE, sig_trap);214215sprintf( str, "%s/lib/mc.ini", getenv("ELMER_POST_HOME") );216217if ( (math_in = fopen( str, "r" ) ) != (FILE *)NULL)218{219doread();220fclose( math_in );221}222223/*224* and finally standard input.225*/226math_in = stdin;227228doread();229230var_free();231com_free();232fnc_free();233const_free();234235mem_free_all();236237#ifdef DEBUG238fclose(fplog);239#endif240#endif241242return; /* done */243}244245char * mtc_domath( char *str )246{247VARIABLE *headsave; /* this should not be here */248249jmp_buf jmp, *savejmp; /* save program context */250251void (*sigfunc)() = (void (*)())signal( SIGINT, sig_trap );252253if ( !str || !*str )254{255str = (char *)doread();256signal( SIGINT, sigfunc );257return math_out_str;258}259260savejmp = jmpbuf;261jmpbuf = &jmp;262263#ifdef DEBUG264fprintf( stderr, "got [%s]\n", str );265#endif266if ( math_out_str ) math_out_str[0] = '\0';267math_out_count = 0;268269/*270* try it271*/272if (*str != '\0')273{274ALLOC_HEAD = (LIST *)NULL;275headsave = (VARIABLE *)VAR_HEAD;276277/*278* normal return takes branch 1,279* error() takes branch 2,280* quit() takes branch 3.281*/282switch (setjmp(*jmpbuf))283{284case 0:285(void)doit( str );286longjmp(*jmpbuf, 1);287break;288289case 1:290break;291292case 2:293VAR_HEAD = (LIST *)headsave;294break;295296case 3:297break;298}299}300301jmpbuf = savejmp;302303signal( SIGINT, sigfunc );304305return math_out_str;306}307308#ifdef _OPENMP309/* Data with thread-local storage cannot be reliably accessed across DLL310borders. Use an accessor function instead. */311LIST * mtc_get_listheaders(void) { return listheaders; }312#endif /* _OPENMP */313314char *doread(void)315/*======================================================================316? doread() is really the main loop of this program. Function reads317| it's input as strings and gives them to function doit() for318| execution. setjmp() function is used for error recovery.319|320| Memory allocated during the lifetime of this function is321| collected to a list represented by the global VARIABLE322| ALLOCLIST *alloc_list. If function error() is called, this323| list is used to deallocate memory. Normally (well I certainly324| hope so) functions which allocate memory deallocate it themselves.325|326| Program stays in this function until an end of file -condition327| is reached or exit- or quit-commands are given.328|329@ jmp_buf *jmpbuf, ALLOC_LIST *alloc_list330& ALLOCMEM, FREEMEM, setjmp(), longjmp()331~ doit(), quit(), error()332^=====================================================================*/333{334VARIABLE *headsave; /* this should not be here */335336jmp_buf jmp, *savejmp; /* save program context */337338char *p, *q; /* buffer for input stream */339340savejmp = jmpbuf;341jmpbuf = &jmp;342343if ( math_out_str ) math_out_str[0] = '\0';344math_out_count = 0;345346p = q = ALLOCMEM(4096);347/*348* try it349*/350while(dogets(p, PMODE_MAIN))351{352if (*p != '\0')353{354ALLOC_HEAD = (LIST *)NULL;355headsave = (VARIABLE *)VAR_HEAD;356357/*358* normal return takes branch 1,359* error() takes branch 2,360* quit() takes branch 3.361*/362switch (setjmp(*jmpbuf))363{364case 0:365(void)doit(p);366longjmp(*jmpbuf, 1);367break;368369case 1:370break;371372case 2:373VAR_HEAD = (LIST *)headsave;374break;375376case 3:377goto ret;378break;379}380}381}382383ret:384385jmpbuf = savejmp;386387FREEMEM(q);388389return math_out_str;390}391392VARIABLE *com_quit(void)393/*======================================================================394? Quit current doread entry by longjumping back to it (nasty).395& longjmp396~ doread397^=====================================================================*/398{399longjmp(*jmpbuf, 3);400401return (VARIABLE *)NULL; /* won't be executed (hopefully) */402}403404int dogets(char *buff, char *prompt)405/*======================================================================406? Get line from input stream. If both input & output streams are407| connected to terminal, this function gives user one of three408| (default) prompts:409|410| MATC>411| - normal prompt (PMODE_MAIN)412| ....>413| - begin end- block is being defined (PMODE_BLOCK)414| ####> (PMODE_CONT)415| - user has given a #-sign as a last character of416| previous line, this line will be concatenated with it417|418| If current comment character is found from input stream, the419| line after this character is discarded. Likewise if current420| system command character is found, the rest of the line is421| passed to system()-call.422|423= line got -> TRUE, EOF -> FALSE424! There should be a way to get an echo when reading from file.425& fprintf(), isatty(), fileno(), strlen(), fgets(), system()426^=====================================================================*/427{428char *ptr = buff, *p; /* Can't get rid of these. */429430if ( !math_in ) return FALSE;431432/*433Try figuring out if input & output streams are434terminals, if they both are, give user a prompt.435*/436if (isatty(fileno(math_in)) && isatty(fileno(math_out)))437PrintOut( "%s", prompt );438439/*440i'm not in the mood to explain this.441*/442*ptr++ = ' ';443444/*445Go for it.446*/447while((ptr = fgets(ptr, 256, math_in)) != NULL)448{449450ptr[strlen(ptr)-1] = '\0';451452/*453* Check if the user wants to continue with this line.454*/455while(ptr[strlen(ptr)-1] == '\\')456{457ptr += strlen(ptr) - 1;458dogets(ptr, PMODE_CONT);459}460461/*462* if there is only spaces in this line,463* don't bother returning it, instead464* let's read afresh, otherwise return.465*/466p = ptr; while(isspace(*p)) p++;467468if (*p != '\0') /* GOOD EXIT HERE */469{470#if 0471/*472* Look for the system character, if found473* pass rest of the line to system()-call474*/475for(p = buff; *p; p++)476{477switch(*p)478{479case SYSTEM:480system(p + 1);481PrintOut("\n");482*p = '\0'; p--;483break;484}485}486#endif487if (*buff != '\0')488return TRUE; /* OR IF WE ARE HONEST, IT'S HERE */489}490491/*492if it's terminal give a prompt.493*/494if (isatty(fileno(math_in)) && isatty(fileno(math_out)))495PrintOut("%s", prompt);496}497498return FALSE;499}500501502void com_init(char *word, int flag_pw, int flag_ce, VARIABLE *(*sub)(),503int minp, int maxp, char *help_text )504/*======================================================================505? Adds commands to global command list.506|507| Parameters:508| char *word509| - the keyword user gives for this command to be executed.510| int flag_pw511| - flag telling if the command can be executed element512| by element using function *(*sub)().513| int flag_ce514| - flag telling if the command can be executed when515| preprocessing if constant arguments516| double *(*sub)()517| - function to be executed when this command is given518| int minp, maxp519| - maximum and minimum number of parameters to command520|521| The global list of available commands is updated (or created if522| nonexistent).523|524& lst_add()525~ *_com_init()526^=====================================================================*/527{528COMMAND *ptr; /* can't get rid of this */529530531/*532Fill the structure...533*/534ptr = (COMMAND *)ALLOCMEM(COMSIZE);535NAME(ptr) = STRCOPY(word);536if (flag_pw)537ptr->flags |= CMDFLAG_PW;538if (flag_ce)539ptr->flags |= CMDFLAG_CE;540ptr->minp = minp;541ptr->maxp = maxp;542ptr->sub = sub;543ptr->help = help_text;544545/*546...and update the list.547*/548lst_add(COMMANDS, (LIST *)ptr);549550return;551}552553void com_free(void)554/*======================================================================555? Deletes the list of commands and frees associated memory.556|557& lst_purge()558^=====================================================================*/559{560/*561Give memory back to system562*/563lst_purge(COMMANDS);564565return;566}567568COMMAND *com_check(char *str)569/*======================================================================570? Look for command from COMMANDS list by name.571|572= COMMAND *NULL if does not exist, pointer to command otherwise573& lst_find()574^=====================================================================*/575{576return (COMMAND *)lst_find(COMMANDS, str);577}578579VARIABLE *com_help( VARIABLE *ptr )580/*======================================================================581? Print list of commands and user defined functions from global lists.582|583! The command to get here is "help" but it really is not very helpful.584|585& lst_print()586^=====================================================================*/587{588COMMAND *cmd;589FUNCTION *fnc;590char *name;591592if ( !ptr )593{594595lst_print(COMMANDS);596lst_print(FUNCTIONS);597598} else {599600name = var_to_string( ptr );601602if ( (cmd = com_check( name ) ) != (COMMAND *)NULL )603{604605if ( cmd->help )606PrintOut( "\n%s\n", cmd->help );607else608PrintOut( "\nSorry: no help available on [%s].\n", name );609610} else if ( (fnc = fnc_check( name ) ) != (FUNCTION *)NULL )611{612613if ( fnc->help )614PrintOut( "\n%s", fnc->help );615else616PrintOut( "\nSorry: no help available on [%s].\n", name );617618} else {619620error( "help: symbol not found: [%s]\n", name );621622}623624FREEMEM( name );625}626627return (VARIABLE *)NULL;628}629630VARIABLE *com_pointw(double (*sub)(), VARIABLE *ptr)631/*======================================================================632? This routine does a function call (*sub)(), for each element in633| matrix given by ptr.634|635= a temporary VARIABLE for which M(res, i, j) = (*sub)(M(ptr, i, j)636& var_temp_new(), *(sub)()637^=====================================================================*/638{639VARIABLE *res,*ptr2; /* pointer to result structure */640641double *a, *a2, *a3, *b; /* pointer to matrices */642int n, m, sz; /* matrix dimensions */643644int i; /* loop index */645646/*647Get space for result and ...648*/649n = NROW(ptr); m = NCOL(ptr);650res = var_temp_new(TYPE(ptr) ,n , m);651652sz = n*m;653a = MATR(ptr); b = MATR(res);654655/*656...to action.657*/658ptr2 = NEXT(ptr);659if(ptr2)660{661if(n!=NROW(ptr2)||m!=NCOL(ptr2))662{663error("Pointwise function arguments must all be of same size.");664}665a2 = MATR(ptr2);666667ptr2 = NEXT(ptr2);668if(ptr2)669{670if(n!=NROW(ptr2)||m!=NCOL(ptr2))671{672error("Pointwise function arguments must all be of same size,");673}674if(NEXT(ptr2))675{676error("Currently at most three arguments for pointwise functions allowed, sorry.");677}678a3 = MATR(ptr2);679for(i = 0; i < sz; i++)680*b++ = ((double (*)(double, double, double)) sub)(*a++, *a2++, *a3++);681}682else683{684for(i = 0; i < sz; i++)685*b++ = ((double (*)(double, double)) sub)(*a++, *a2++);686}687}688else689{690for(i = 0; i < sz; i++)691*b++ = ((double (*)(double)) sub)(*a++);692}693694return res;695}696697VARIABLE *com_el(VARIABLE *ptr)698/*======================================================================699? Extracts specified elements from a matrix. Indexes are given by two700| column vectors. The values of the elements of these vectors give701| the required indexes. If there is only one index vector given702| it is assumed to be column index and row index is set to scalar 0.703|704| If matrix x is, for example,705|706| 1 2707| 3 4708|709| you get the first row by710|711| x[0, 0 1]712|713| or by714|715| x(0 1)716|717= A new temporary VARIABLE, whose size equals to718| number of row indexes times number of column indexes.719|720& var_temp_new(), var_delete_temp()721^=====================================================================*/722{723VARIABLE *res, /* result ... */724*par = NEXT(ptr); /* pointer to list of VARIABLES */725/* containing indexes */726727static double defind = 0.0;728#pragma omp threadprivate (defind)729double *ind1 = &defind, *ind2;730731int i, j, k, /* loop indexes */732rows, cols, /* no. of rows and columns in the matrix */733/* to be indexed. */734size1 = 1, size2,735ind;736737rows = NROW(ptr); cols = NCOL(ptr);738739/*740* check if scalar ....741*/742if (rows == 1 && cols == 1)743{744if (*MATR(par) != 0) error("Index out of bounds.\n");745if (NEXT(par) != NULL)746if (*MATR(NEXT(par)) != 0) error("Index out of bounds.\n");747res = var_temp_new(TYPE(ptr),1,1);748*MATR(res) = *MATR(ptr);749return res;750}751752/*753The matrix will be indexed by two column vectors.754If there is just one assume it's column index and755make rowindex 0.756*/757if (NEXT(par) == NULL)758{759if (NROW(par) == rows && NCOL(par) == cols)760{761int logical = TRUE,762onecount=0;763764double *dtmp;765766dtmp = MATR(par);767for(i = 0; i < NROW(par)*NCOL(par); i++)768if (dtmp[i] == 0)769{770}771else if (dtmp[i] == 1)772{773onecount++;774}775else776{777logical = FALSE;778break;779}780781if (logical)782{783if (onecount == 0) return NULL;784785res = var_temp_new(TYPE(ptr),1,onecount);786for(i=0,k=0; i < rows; i++)787for(j=0; j < cols; j++)788if (M(par,i,j) == 1)789{790memcpy(&M(res,0,k++),&M(ptr,i,j),sizeof(double));791}792return res;793}794}795796ind2 = MATR(par); size2 = NCOL(par);797cols *= rows; rows = 1;798}799else800{801ind1 = MATR(par); size1 = NCOL(par);802size2 = NCOL(NEXT(par));803ind2 = MATR(NEXT(par));804}805806/*807Space for result808*/809res = var_temp_new(TYPE(ptr), size1, size2);810811/*812Extract the values (try making sense out of that813if you feel like it).814*/815for(i = 0; i < size1; i++)816{817ind = (int)ind1[i];818for(j = 0; j < size2; j++)819if (ind < rows && (int)ind2[j] < cols)820memcpy(&M(res,i,j),&M(ptr,ind,(int)ind2[j]),sizeof(double));821else822error("Index out of bounds.\n");823}824825return res;826}827828VARIABLE *com_source(VARIABLE *ptr)829/*======================================================================830? Redirect input stream to a file, whose name is given.831|832@ FILE *math_in833& ALLOCMEM, FREEMEM, fopen(), fclose(), error()834^=====================================================================*/835{836char *name; /* Hold converted string (file name) */837838FILE *save_in = math_in; /* Save previous input stream until */839/* we are done with the new file. */840841/*842convert the file name from ptr.843*/844name = var_to_string(ptr);845846/*847Execute the file.848*/849if ((math_in = fopen(name,"r")) != NULL)850{851/* PrintOut("Executing commands from file, %s...\n", name); */852doread();853fclose(math_in);854}855else856{857PrintOut( "Source: Can't open file, %s.\n",name );858}859860math_in = save_in;861FREEMEM(name);862863return (VARIABLE *)NULL;864}865866867VARIABLE *com_apply(VARIABLE *ptr)868/*======================================================================869? Executes given string.870|871& ALLOCMEM, FREEMEM, doit()872^=====================================================================*/873{874VARIABLE *res; /* result pointer */875876char *p, *q; /* holds the string to be executed, after */877/* conversion from structure VARIABLE * */878879int i, j; /* just loop indexes */880881882/*883Allocate space for the string...884*/885p = q = ALLOCMEM(NROW(ptr) * NCOL(ptr) + 1);886887/*888... convert it ...889*/890for(i = 0; i < NROW(ptr); i++)891for(j = 0; j < NCOL(ptr); j++)892*p++ = (char)M(ptr,i,j);893894*p = '\0';895896/*897... and try executing it.898*/899res = doit( q );900901FREEMEM(q);902903return res;904}905906void mem_free(void *mem)907/*======================================================================908? Free memory given by argument, and unlink it from allocation list.909| Currently FREEMEM(ptr) is defined to be mem_free(ptr).910|911& free()912~ mem_alloc(), mem_free_all()913^=====================================================================*/914{915ALLOC_LIST *lst;916917#ifdef DEBUG918tot--; fprintf(fplog,"free addr: %d total: %d\n", ALLOC_LST(mem), tot);919fflush( fplog );920#endif921/*922if the list is empty return923*/924if ( (lst = (ALLOC_LIST *)ALLOC_HEAD) == (ALLOC_LIST *)NULL )925{926#if 1927/* ????? */928free( ALLOC_LST(mem) );929#else930fprintf( stderr, "SHOULD THIS HAPPEN ????\n" );931#endif932return;933}934935/*936* it's not the header, look if it's in list at all937*/938if (ALLOC_PTR(lst) != mem)939{940941for(; NEXT(lst); lst = NEXT(lst))942{943if (ALLOC_PTR(NEXT(lst)) == mem) break;944}945946/*947* item was not found from the list. free ptr and return.948*/949if (NEXT(lst) == (ALLOC_LIST *)NULL)950{951free(ALLOC_LST(mem));952return;953}954955/*956* unlink957*/958NEXT(lst) = NEXT(NEXT(lst));959}960961/*962* item was the header, unlink it963*/964else965ALLOC_HEAD = NEXT(ALLOC_HEAD);966967/*968* and at last return memory back to system969*/970free(ALLOC_LST(mem));971972return;973}974975void mem_free_all(void)976/*======================================================================977? Free all memory allocated since last entry of doread.978| (actually free all memory from list ALLOCATIONS).979|980~ mem_alloc(), mem_free(), doread(), error()981^=====================================================================*/982{983ALLOC_LIST *lst, *lstn;984985for(lst = (ALLOC_LIST *)ALLOC_HEAD; lst;)986{987#ifdef DEBUG988tot--; fprintf(fplog,"freeall addr: %d total: %d\n", lst, tot);989fflush( fplog );990#endif991lstn = NEXT(lst);992free( (char *)lst );993lst = lstn;994}995996ALLOC_HEAD = (LIST *)NULL; /* security */997998return;999}10001001void *mem_alloc(size_t size)1002/*======================================================================1003? Allocate memory and link it to memory allocation list.1004|1005~ calloc(), free(), error()1006^=====================================================================*/1007{1008ALLOC_LIST *lst;10091010/*1011* try allocating memory1012*/1013if ((lst = (ALLOC_LIST *)calloc(size+sizeof(ALLOC_LIST), 1)) != NULL)1014{1015NEXT(lst) = (ALLOC_LIST *)ALLOC_HEAD; ALLOC_HEAD = (LIST *)lst;1016}1017else1018error("Can't alloc mem.\n");10191020#ifdef DEBUG1021tot++; fprintf(fplog,"alloc addr: %d size: %d total: %d\n",1022lst, size, tot);1023fflush( fplog );1024#endif1025return ALLOC_PTR(lst);1026}102710281029