diff --git a/CMakeLists.txt b/CMakeLists.txt index d5dd50b3a6..09b6615107 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,8 +12,10 @@ if(APPLE) # message("you need to have gcc-gfrotran installed using HomeBrew") - set(CMAKE_C_COMPILER "/usr/local/bin/gcc-9") - set(CMAKE_CXX_COMPILER "/usr/local/bin/g++-9") + # set(CMAKE_C_COMPILER "/usr/bin/gcc") + # set(CMAKE_CXX_COMPILER "/usr/bin/g++") + set(CMAKE_C_COMPILER "/usr/local/bin/gcc-10") + set(CMAKE_CXX_COMPILER "/usr/local/bin/g++-10") set(CMAKE_Fortran_COMPILER "/usr/local/bin/gfortran") # set(BLA_VENDOR "OpenBLAS") # option(HUNTER_ENABLED "Enable Hunter package manager support" OFF) @@ -23,12 +25,16 @@ endif() + + IF(NOT CMAKE_BUILD_TYPE) SET(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Build type (Release, Debug, RelWithDebugInfo, MinSizeRel)") ENDIF() PROJECT(Elmer Fortran C CXX) -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.9) + +# CMAKE_VERSION seems to require this in minimum +CMAKE_MINIMUM_REQUIRED(VERSION 3.0.2) IF(APPLE) SET(CMAKE_MACOSX_RPATH 1) @@ -122,8 +128,8 @@ MARK_AS_ADVANCED(ELMER_INSTALL_LIB_DIR) ENABLE_TESTING() -SET(ELMER_FEM_MAJOR_VERSION 8) -SET(ELMER_FEM_MINOR_VERSION 4) +SET(ELMER_FEM_MAJOR_VERSION 9) +SET(ELMER_FEM_MINOR_VERSION 0) SET(ELMER_FEM_VERSION ${ELMER_FEM_MAJOR_VERSION}.${ELMER_FEM_MINOR_VERSION}) @@ -178,13 +184,33 @@ IF(WITH_OpenMP) OpenMP_Fortran_FLAGS OpenMP_CXX_FLAGS ) - - FIND_PACKAGE(OpenMP REQUIRED) + # Add OpenMP flags to compilation flags - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + # if(APPLE) + # if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + # set(OpenMP_C "${CMAKE_C_COMPILER}") + # set(OpenMP_C_FLAGS "-fopenmp=libomp -Wno-unused-command-line-argument") + # set(OpenMP_C_LIB_NAMES "libomp" "libgomp" "libiomp5") + # set(OpenMP_libomp_LIBRARY ${OpenMP_C_LIB_NAMES}) + # set(OpenMP_libgomp_LIBRARY ${OpenMP_C_LIB_NAMES}) + # set(OpenMP_libiomp5_LIBRARY ${OpenMP_C_LIB_NAMES}) + # endif() + # if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + # set(OpenMP_CXX "${CMAKE_CXX_COMPILER}") + # set(OpenMP_CXX_FLAGS "-fopenmp=libomp -Wno-unused-command-line-argument") + # set(OpenMP_CXX_LIB_NAMES "libomp" "libgomp" "libiomp5") + # set(OpenMP_libomp_LIBRARY ${OpenMP_CXX_LIB_NAMES}) + # set(OpenMP_libgomp_LIBRARY ${OpenMP_CXX_LIB_NAMES}) + # set(OpenMP_libiomp5_LIBRARY ${OpenMP_CXX_LIB_NAMES}) + # endif() + # else() + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + # endif() + FIND_PACKAGE(OpenMP REQUIRED) + # Test compiler support for OpenMP 4.0 features used INCLUDE(testOpenMP40) IF(CMAKE_Fortran_COMPILER_SUPPORTS_OPENMP40) @@ -199,6 +225,15 @@ IF(WITH_OpenMP) ENDIF() ENDIF() +# Get rid of the annoying rank mismatch warning +IF("${CMAKE_Fortran_COMPILER_ID}" MATCHES "GNU") + IF(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 9.9) + SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fallow-argument-mismatch") +# SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -std=legacy") + ENDIF() +ENDIF() + + IF(WITH_MPI) # Advanced properties MARK_AS_ADVANCED( diff --git a/ElmerGUI/Application/CMakeLists.txt b/ElmerGUI/Application/CMakeLists.txt index 5dac55c4e9..f6ee9e0f89 100644 --- a/ElmerGUI/Application/CMakeLists.txt +++ b/ElmerGUI/Application/CMakeLists.txt @@ -102,6 +102,8 @@ SET(SOURCES src/bodypropertyeditor.cpp twod/twodview.cpp twod/curveeditor.cpp) +ADD_DEFINITIONS(-DEG_PLUGIN) + IF(WITH_QWT) SET(HEADERS ${HEADERS} src/convergenceview.h) SET(SOURCES ${SOURCES} src/convergenceview.cpp) diff --git a/ElmerGUI/Application/edf-extra/vectorhelmholtz.xml b/ElmerGUI/Application/edf-extra/vectorhelmholtz.xml index 99c5d02bd4..42274f45e5 100644 --- a/ElmerGUI/Application/edf-extra/vectorhelmholtz.xml +++ b/ElmerGUI/Application/edf-extra/vectorhelmholtz.xml @@ -36,24 +36,24 @@ Material properties - Inverse Relative Permeability + Relative Permittivity String - Give the inverse relative permeability compared to vacuum (real part). + Give the relative permittivity of medium (real part). - Inverse Relative Permeability im + Relative Permittivity im String - Give the inverse relative permeability compared to vacuum (imag part). + Give the relative permittivity of medium (imag part). - Relative Permittivity + Relative Reluctivity String - Give the relative permittivity of medium (real part). + Give the relative reluctivity of the medium (real part). - Relative Permittivity im + Relative Reluctivity im String - Give the relative permittivity of medium (imag part). + Give the relative reluctivity of the medium (imag part). @@ -308,7 +308,7 @@ Calculate Poynting Vector - Calculate Div Poynting Vector + Calculate Div of Poynting Vector Logical Calculate Divergence of Poyntings vector diff --git a/ElmerGUI/Application/plugins/egconvert.cpp b/ElmerGUI/Application/plugins/egconvert.cpp index 7e5324dc2d..c40d26f4c8 100644 --- a/ElmerGUI/Application/plugins/egconvert.cpp +++ b/ElmerGUI/Application/plugins/egconvert.cpp @@ -31,16 +31,19 @@ #include #include #include +#include #include "egutils.h" #include "egdef.h" #include "egtypes.h" #include "egmesh.h" -#include "egnative.h" #include "egconvert.h" -#define getline fgets(line,MAXLINESIZE,in) +#define GETLINE ioptr=fgets(line,MAXLINESIZE,in) +#define GETLONGLINE ioptr=fgets(longline,LONGLINESIZE,in) +static int linenumber; +static char *ioptr; static int Getrow(char *line1,FILE *io,int upper) { @@ -51,8 +54,9 @@ static int Getrow(char *line1,FILE *io,int upper) line0[i] = ' '; newline: - charend = fgets(line0,MAXLINESIZE,io); + linenumber += 1; + isend = (charend == NULL); if(isend) return(1); @@ -72,9 +76,6 @@ static int Getrow(char *line1,FILE *io,int upper) return(0); } - - - static int GetrowDouble(char *line1,FILE *io) { int i,isend; @@ -84,8 +85,9 @@ static int GetrowDouble(char *line1,FILE *io) line0[i] = ' '; newline: - charend = fgets(line0,MAXLINESIZE,io); + linenumber += 1; + isend = (charend == NULL); if(isend) return(1); @@ -116,6 +118,8 @@ static int Comsolrow(char *line1,FILE *io) line0[i] = ' '; charend = fgets(line0,MAXLINESIZE,io); + linenumber += 1; + isend = (charend == NULL); if(isend) return(1); @@ -135,6 +139,7 @@ static void FindPointParents(struct FemType *data,struct BoundaryType *bound, int sideind[MAXNODESD1],elemsides,side,sidenodes,hit,nohits; int *elemhits; + info = TRUE; sideelem = 0; @@ -152,21 +157,24 @@ static void FindPointParents(struct FemType *data,struct BoundaryType *bound, printf("Boundary types are in interval [%d, %d]\n",minboundary,maxboundary); printf("Boundary nodes are in interval [%d, %d]\n",minnode,maxnode); } + indx = Ivector(1,data->noknots); + printf("Allocating hit table of size: %d\n",data->noknots); elemhits = Ivector(1,data->noknots); for(i=1;i<=data->noknots;i++) elemhits[i] = 0; + for(elemind=1;elemind<=data->noelements;elemind++) { elemtype = data->elementtypes[elemind]; elemsides = elemtype % 100; - + for(i=0;itopology[elemind][i]] += 1; + j = data->topology[elemind][i]; + elemhits[j] += 1; } } - for(boundarytype=minboundary;boundarytype <= maxboundary;boundarytype++) { int boundfirst,bchits,bcsame,sideelemtype2; int sideind2[MAXNODESD1]; @@ -187,17 +195,13 @@ static void FindPointParents(struct FemType *data,struct BoundaryType *bound, elemsides = elemtype / 100; if(elemsides == 8) elemsides = 6; else if(elemsides == 5) elemsides = 4; - else if(elemsides == 6) elemsides = 5; - - if(0) printf("ind=%d type=%d sides=%d\n",elemind,elemtype,elemsides); - + else if(elemsides == 6 || elemsides == 7) elemsides = 5; + /* Check whether the bc nodes occupy every node in the selected side */ for(side=0;sidetopology[elemind][i]); + printf("\n"); + + hit = TRUE; + } + + if(hit == TRUE) { @@ -254,6 +275,10 @@ static void FindPointParents(struct FemType *data,struct BoundaryType *bound, } sideelem += 1; + + if( sideelem > bound->nosides ) { + printf("There are more side elements than allocated for (%d vs. %d)\n",sideelem,bound->nosides); + } bound->parent[sideelem] = elemind; bound->side[sideelem] = side; bound->parent2[sideelem] = 0; @@ -275,7 +300,7 @@ static void FindPointParents(struct FemType *data,struct BoundaryType *bound, if(info) printf("Found %d side elements formed by %d points.\n", sideelem,boundarynodes); - bound->nosides = sideelem; + bound->nosides = MIN( sideelem, bound->nosides ); return; } @@ -291,24 +316,26 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, results in ABAQUS format. */ { - int noknots,noelements,elemcode,maxnodes,material; - int mode,allocated,nvalue,nvalue2,maxknot,nosides; - int boundarytype,boundarynodes,elsetactive; - int *nodeindx = NULL,*boundindx = NULL; - + int noknots,noelements,elemcode,maxnodes,material,maxelem,nodeoffset; + int mode,allocated,nvalue,nvalue2,maxknot,nosides,elemnodes,ncum; + int boundarytype,boundarynodes,elsetactive,elmatactive,cont; + int *nodeindx=NULL,*boundindx=NULL,*materials=NULL,*elemindx=NULL; + char *pstr; char filename[MAXFILESIZE]; char line[MAXLINESIZE]; - int i,j,*ind = NULL; + int i,j,k,*ind=NULL; FILE *in; Real rvalues[MAXDOFS]; int ivalues[MAXDOFS],ivalues0[MAXDOFS]; - + int setmaterial; + int debug,firstline; + char entityname[MAXNAMESIZE]; strcpy(filename,prefix); if ((in = fopen(filename,"r")) == NULL) { AddExtension(prefix,filename,"inp"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadAbaqusInput: opening of the ABAQUS-file '%s' wasn't succesfull !\n", + printf("LoadAbaqusInput: opening of the ABAQUS-file '%s' wasn't successful !\n", filename); return(1); } @@ -319,18 +346,23 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, allocated = FALSE; maxknot = 0; + maxelem = 0; elsetactive = FALSE; - + elmatactive = FALSE; + /* Because the file format doesn't provide the number of elements or nodes the results are read twice but registered only in the second time. */ + debug = FALSE; + omstart: mode = 0; maxnodes = 0; noknots = 0; noelements = 0; + nodeoffset = 0; elemcode = 0; boundarytype = 0; boundarynodes = 0; @@ -339,65 +371,170 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, for(;;) { - /* getline; */ + /* GETLINE; */ if (Getrow(line,in,TRUE)) goto end; /* if(!line) goto end; */ /* if(strstr(line,"END")) goto end; */ - if(strstr(line,"**")) { - if(info && !allocated) printf("comment: %s",line); - } - else if(strrchr(line,'*')) { - if(strstr(line,"HEAD")) { - mode = 1; + + if(strrchr(line,'*')) { + if( mode == 2 ) { + printf("Number of nodes so far: %d\n",noknots); } - else if(strstr(line,"NODE")) { - if(strstr(line,"SYSTEM=R")) data->coordsystem = COORD_CART2; - if(strstr(line,"SYSTEM=C")) data->coordsystem = COORD_AXIS; - if(strstr(line,"SYSTEM=P")) data->coordsystem = COORD_POLAR; - mode = 2; + else if( mode == 3 ) { + printf("Number of elements so far: %d\n",noelements); + if(elmatactive) { + nodeoffset = noknots; + printf("Node offset is %d\n",nodeoffset); + } } - else if(strstr(line,"ELEMENT")) { - if(!elsetactive) material++; - if(strstr(line,"S3R") || strstr(line,"STRI3")) - elemcode = 303; - else if(strstr(line,"2D4") || strstr(line,"SP4") || strstr(line,"AX4") - || strstr(line,"S4") || strstr(line,"CPE4")) - elemcode = 404; - else if(strstr(line,"2D8") || strstr(line,"AX8")) - elemcode = 408; - else if(strstr(line,"3D4")) - elemcode = 504; - else if(strstr(line,"3D5")) - elemcode = 605; - else if(strstr(line,"3D6")) - elemcode = 706; - else if(strstr(line,"3D8")) - elemcode = 808; - else if(strstr(line,"3D20")) - elemcode = 820; - else - printf("Unknown element code: %s\n",line); - if(maxnodes < elemcode%100) maxnodes = elemcode%100; - mode = 3; - if(1) printf("Loading elements of type %d starting from element %d.\n", - elemcode,noelements); + if( strstr(line,"**") ) { + if( strstr(line,"**HWCOLOR") ) + mode = 10; + else if( strstr(line,"**HWNAME") ) + mode = 10; + else if( strstr(line,"**HMASSEM") ) + mode = 10; + else if( strstr(line,"**HM_COMP") ) + mode = 10; + else if( strstr(line,"**HM_PROP") ) + mode = 10; + else + if(info && !allocated) printf("comment: %s",line); + } + else if(strstr(line,"HEAD")) { + mode = 1; + } + else if(strstr(line,"*NODE")) { + if(pstr = strstr(line,"NODE OUTPUT")) { + mode = 10; + } + else { + if(strstr(line,"SYSTEM=R")) data->coordsystem = COORD_CART2; + if(strstr(line,"SYSTEM=C")) data->coordsystem = COORD_AXIS; + if(strstr(line,"SYSTEM=P")) data->coordsystem = COORD_POLAR; + mode = 2; + } } - else if(strstr(line,"BOUNDARY") || strstr(line,"CLOAD")) { + else if(strstr(line,"*ELEMENT")) { + if(pstr = strstr(line,"ELEMENT OUTPUT")) { + mode = 10; + } + else { + if(!(elsetactive || elmatactive)) material++; + if(strstr(line,"S3") || strstr(line,"STRI3") || strstr(line,"M3D3")) + elemcode = 303; + else if(strstr(line,"2D4") || strstr(line,"SP4") || strstr(line,"AX4") + || strstr(line,"S4") || strstr(line,"CPE4")) + elemcode = 404; + else if(strstr(line,"2D8") || strstr(line,"AX8") || strstr(line,"DS8") ) + elemcode = 408; + else if(strstr(line,"3D4")) + elemcode = 504; + else if(strstr(line,"3D5")) + elemcode = 605; + else if(strstr(line,"3D6")) + elemcode = 706; + else if(strstr(line,"3D15")) + elemcode = 715; + else if(strstr(line,"3D8")) + elemcode = 808; + else if(strstr(line,"3D20")) + elemcode = 820; + else + printf("Unknown element code: %s\n",line); + + if(pstr = strstr(line,"ELSET=")) { + if(allocated) { + printf("Loading element set %d from %s",material,pstr+6); + } + } + + elemnodes = elemcode % 100; + maxnodes = MAX( maxnodes, elemnodes); + mode = 3; + if(allocated) { + printf("Loading elements of type %d starting from element %d.\n", + elemcode,noelements); + if(!(elsetactive || elmatactive)) { + sscanf(pstr+6,"%s",entityname); + strcpy(data->bodyname[material],entityname); + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + } + } + + firstline = TRUE; + } + } + else if( strstr(line,"BOUNDARY") ) { boundarytype++; mode = 4; + if(allocated) { + printf("Treating keyword BOUNDARY\n"); + } + } + else if( strstr(line,"SOLID SECTION") ) { + /* Have this here since solid section may include ELSET */ + mode = 10; } - else if(strstr(line,"NSET")) { + else if( strstr(line,"MEMBRANE SECTION") ) { + /* Have this here since solid section may include ELSET */ + mode = 10; + } + else if( strstr(line,"CLOAD") ) { + boundarytype++; - mode = 5; + mode = 4; + if(allocated) { + printf("Treating keyword CLOAD\n"); + } + } + else if(pstr = strstr(line,"NSET=")) { + if( strstr(line,"ELSET=") ) { + /* Skipping association of ELSET to NSET */ + mode = 10; + } + else { + boundarytype++; + mode = 5; + if(allocated) { + printf("Loading boundary node set %d from: %s",boundarytype,pstr+5); + } + } } - else if(strstr(line,"ELSET")) { + else if(pstr = strstr(line,"ELSET=")) { elsetactive = TRUE; material += 1; mode = 6; + + if(allocated) { + printf("Loading element set %d from %s",material,pstr+6); + sscanf(pstr+6,"%s",entityname); + strcpy(data->bodyname[material],entityname); + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + } + } + else if(pstr = strstr(line,"PART, NAME=")) { + elmatactive = TRUE; + material += 1; + mode = 6; + + if(allocated) { + printf("Loading part name %d from %s",material,pstr+11); + sscanf(pstr+6,"%s",entityname); + strcpy(data->bodyname[material],entityname); + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + } + } + else if(pstr = strstr(line,"HWCOLOR")) { + /* unused command */ + mode = 0; } else { if(!allocated) printf("unknown command: %s",line); @@ -409,52 +546,118 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, switch (mode) { case 1: - if(info) printf("Loading Abacus input file:\n%s",line); + if(info) printf("Loading Abaqus input file:\n%s",line); break; - case 2: + case 2: /* NODE */ nvalue = StringToReal(line,rvalues,MAXNODESD2+1,','); - i = (int)(rvalues[0]+0.5); - if(i == 0) continue; + + if(nvalue != 4) { + printf("line: %s\n",line); + printf("Invalid nvalue = %d\n",nvalue); + } + + i = (int)(rvalues[0]+0.5); noknots++; + if(allocated) { - ind[i] = noknots; - data->x[noknots] = rvalues[1]; - data->y[noknots] = rvalues[2]; - data->z[noknots] = rvalues[3]; + if( debug && (i==1 || i==maxknot) ) { + printf("debug node: %i %d %.3le %.3le %.3le\n",i,noknots,rvalues[1],rvalues[2],rvalues[3]); + } + + i = MAX( i, noknots ); + if(i <= 0 || i > maxknot) { + printf("Invalid node index = %d\n",i); + } + else { + ind[i] = noknots; + data->x[noknots] = rvalues[1]; + data->y[noknots] = rvalues[2]; + data->z[noknots] = rvalues[3]; + } } else { if(maxknot < i) maxknot = i; } break; - case 3: + case 3: /* ELEMENT */ noelements++; - - nvalue = StringToInteger(line,ivalues,MAXNODESD2+1,','); + nvalue = StringToIntegerNoZero(line,ivalues,elemnodes+1,','); + if(allocated) { + if( debug && firstline ) { + printf("debug elem: %d %d %d %d\n",noelements,ivalues[0],elemcode,material); + printf(" topo:"); + for(i=0;ielementtypes[noelements] = elemcode; + data->material[noelements] = material; for(i=0;itopology[noelements][i] = ivalues[i+1]; + + if( nodeoffset ) { + for(i=0;itopology[noelements][i] += nodeoffset; + } + + } + else { + if( maxelem < ivalues[0] ) maxelem = ivalues[0]; } + + ncum = nvalue-1; - if(nvalue < elemcode % 100) { + /* Read 2nd line if needed */ + if(ncum < elemnodes ) { + Getrow(line,in,TRUE); + nvalue = StringToIntegerNoZero(line,ivalues,elemnodes-ncum,','); + if(allocated) { + for(i=0;itopology[noelements][ncum+i] = ivalues[i]; + } + ncum = ncum + nvalue; + } + + /* Be prepared for 3rd line as well */ + if(ncum < elemnodes ) { Getrow(line,in,TRUE); + nvalue = StringToIntegerNoZero(line,ivalues,elemnodes-ncum,','); if(allocated) { - if(ivalues[nvalue-1] == 0) nvalue--; - nvalue2 = StringToInteger(line,ivalues,MAXNODESD2+1,','); - for(i=0;itopology[noelements][nvalue-1+i] = ivalues[i]; + for(i=0;itopology[noelements][ncum+i] = ivalues[i]; + } + ncum = ncum + nvalue; + } + if(ncum != elemnodes) printf("ncum = %d vs. %d\n",ncum,elemnodes); + + if( allocated ) { + j = FALSE; + for(i=0;itopology[noelements][i]) j = TRUE; + + if(j) { + printf("zero in this element\n"); + printf("element = %d %d\n",noelements,elemnodes); + for(i=0;itopology[noelements][i]); + printf("\n"); } } + break; case 4: - nvalue = StringToInteger(line,ivalues,MAXNODESD2+1,','); + nvalue = StringToInteger(line,ivalues,2,','); - if(ivalues[0] == ivalues0[0] & ivalues[1] != ivalues0[1]) continue; + if(ivalues[0] == ivalues0[0] && ivalues[1] != ivalues0[1]) continue; ivalues0[0] = ivalues[0]; ivalues0[1] = ivalues[1]; @@ -465,8 +668,8 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, } break; - case 5: - nvalue = StringToInteger(line,ivalues,10,','); + case 5: /* NSET */ + nvalue = StringToIntegerNoZero(line,ivalues,10,','); if(allocated) { for(i=0;imaterial[j] = material; + materials[j] = material; } } break; + case 10: + /* Doing nothing */ + break; + + default: printf("Unknown case: %d\n",mode); } @@ -500,10 +708,11 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, if(allocated == TRUE) { - if(info) printf("The mesh was loaded from file %s.\n",filename); + int errcount,okcount; - FindPointParents(data,bound,boundarynodes,nodeindx,boundindx,info); + if(info) printf("The mesh was loaded from file %s.\n",filename); + /* ABAQUS format does not expect that all numbers are used when numbering the elements. Therefore the nodes must be renumberred from 1 to noknots. */ @@ -511,17 +720,64 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, if(noknots != maxknot) { if(info) printf("There are %d nodes but maximum index is %d.\n", noknots,maxknot); - if(info) printf("Renumbering elements\n"); - for(j=1;j<=noelements;j++) - for(i=0;i < data->elementtypes[j]%100;i++) - data->topology[j][i] = ind[data->topology[j][i]]; + if(info) printf("Renumbering %d elements\n",noelements); + errcount = 0; + okcount = 0; + for(j=1;j<=noelements;j++) { + elemcode = data->elementtypes[j]; + elemnodes = elemcode % 100; + for(i=0;i < elemnodes;i++) { + k = data->topology[j][i]; + if(k<=0) { + printf("err elem ind: %d %d %d %d\n",j,elemcode,i,k); + errcount++; + } + else { + data->topology[j][i] = ind[k]; + okcount++; + } + } + } + printf("There are %d positive and %d non-positive indexes in elements!\n",okcount,errcount); + + if(info) printf("Renumbering %d nodes in node sets\n",boundarynodes); + errcount = 0; + okcount = 0; + for(j=1;j<=boundarynodes;j++) { + k = nodeindx[j]; + if(k<=0 || k > maxknot) { + printf("err node set ind: %d %d\n",j,k); + errcount++; + } + else { + nodeindx[j] = ind[k]; + okcount++; + } + } + printf("There are %d positive and %d non-positive indexes in node sets!\n",okcount,errcount); } + if(elsetactive) { + for(i=1;i<=noelements;i++) { + j = elemindx[i]; + data->material[i] = materials[j]; + } + } + + ElementsToBoundaryConditions(data,bound,FALSE,info); + free_ivector(ind,1,maxknot); - free_Ivector(nodeindx,1,boundarynodes); - free_Ivector(boundindx,1,boundarynodes); + free_ivector(materials,1,maxelem); + free_Ivector(elemindx,1,noelements); + if( boundarynodes > 0 ) { + printf("Number of nodes in boundary sets: %d\n",boundarynodes); + free_Ivector(nodeindx,1,boundarynodes); + free_Ivector(boundindx,1,boundarynodes); + } + fclose(in); + return(0); } @@ -529,19 +785,30 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, data->noknots = noknots; data->noelements = noelements; data->maxnodes = maxnodes; - data->dim = 3; + data->dim = 3; if(info) printf("Allocating for %d knots and %d %d-node elements.\n", noknots,noelements,maxnodes); AllocateKnots(data); + + elemindx = Ivector(1,noelements); + for(i=1;i<=noelements;i++) + elemindx[i] = 0; + + printf("Number of boundary nodes: %d\n",boundarynodes); + if( boundarynodes > 0 ) { + nodeindx = Ivector(1,boundarynodes); + boundindx = Ivector(1,boundarynodes); + } - nosides = 2*boundarynodes; - printf("There are %d boundary nodes, thus allocating %d elements\n", - boundarynodes,nosides); - AllocateBoundary(bound,nosides); - nodeindx = Ivector(1,boundarynodes); - boundindx = Ivector(1,boundarynodes); - + printf("Maximum element index in file: %d\n",maxelem); + maxelem = MAX( maxelem, noelements ); + materials = ivector(1,maxelem); + for(i=1;i<=maxelem;i++) + materials[i] = 0; + + printf("Maximum node index in file: %d\n",maxknot); + maxknot = MAX( maxknot, noknots ); ind = ivector(1,maxknot); for(i=1;i<=maxknot;i++) ind[i] = 0; @@ -551,6 +818,272 @@ int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound, } +static int ReadAbaqusField(FILE *in,char *buffer,int *argtype,int *argno) +/* This subroutine reads the Abaqus file format and tries to make + sense out of it. + */ +{ + int i,val,digits; + static int maxargno=0,mode=0; + + val = fgetc(in); + + if(val==EOF) return(-1); + if(val=='\n') val = fgetc(in); + + if(val=='*') { + if(0) printf("start field\n"); + if((*argno) != maxargno) + printf("The previous field was of wrong length, debugging time!\n"); + (*argno) = 0; + mode = 0; + val = fgetc(in); + if(val=='\n') val = fgetc(in); + } + + if(val=='I') { + for(i=0;i<2;i++) { + val = fgetc(in); + if(val=='\n') val = fgetc(in); + buffer[i] = val; + } + buffer[2] = '\0'; + digits = atoi(buffer); + for(i=0;imaxnodes = 9; + data->dim = 3; + + for(;;) { + + mode = ReadAbaqusField(in,buffer,&argtype,&argno); + if(0) printf("%d %d: buffer: %s\n",argtype,argno,buffer); + + switch (mode) { + + case -1: + goto jump; + + case 0: + break; + + case 1921: + /* General info */ + if(argno == 3) printf("Reading output file for Abaqus %s\n",buffer); + else if(argno == 4) printf("Created on %s",buffer); + else if(argno == 5) printf("%s",buffer); + else if(argno == 6) printf("%s\n",buffer); + else if(argno == 7) data->noelements = atoi(buffer); + else if(argno == 8 && allocated == FALSE) { + data->noknots = atoi(buffer); + allocated = TRUE; + AllocateKnots(data); + indx = Ivector(0,2 * data->noknots); + for(i=1;i<=2*data->noknots;i++) + indx[i] = 0; + } + break; + + case 1900: + /* Element definition */ + if(argno == 3) elemno = atoi(buffer); + else if(argno == 4) { + if(strstr(buffer,"2D4") || strstr(buffer,"SP4") || strstr(buffer,"AX4")) + data->elementtypes[elemno] = 404; + else if(strstr(buffer,"2D8") || strstr(buffer,"AX8") || strstr(buffer,"S8R5")) + data->elementtypes[elemno] = 408; + else if(strstr(buffer,"3D8")) + data->elementtypes[elemno] = 808; + else printf("Unknown element code: %s\n",buffer); + } + else if(argno >= 5) + data->topology[elemno][argno-5] = atoi(buffer); + break; + + case 1901: + /* Node definition */ + if(argno == 3) { + knotno++; + if(atoi(buffer) > 2*data->noknots) + printf("LoadAbaqusOutput: allocate more space for indx.\n"); + else + indx[atoi(buffer)] = knotno; + } + if(argno == 4) sscanf(buffer,"%le",&(data->x[knotno])); + if(argno == 5) sscanf(buffer,"%le",&(data->y[knotno])); + if(argno == 6) sscanf(buffer,"%le",&(data->z[knotno])); + break; + + case 1933: + /* Element set */ + if(argno == 3) { + elset++; + strcpy(data->bodyname[elset],buffer); + } + case 1934: + /* Element set continuation */ + if(argno > 3) { + elemno = atoi(buffer); + data->material[elemno] = elset; + } + break; + + case 2001: + /* Just ignore */ + break; + + case 1: + if(argno == 3) knotno = indx[atoi(buffer)]; + if(argno == 5) secno = atoi(buffer); + break; + + case 2: + if(prevdog != mode) { + prevdog = mode; + nodogs++; + CreateVariable(data,nodogs,1,0.0,"Temperature",FALSE); + } + break; + + /* Read vectors in nodes in elements */ + case 11: + if(prevdog != mode) { + prevdog = mode; + nodogs++; + CreateVariable(data,nodogs,3,0.0,"Stress",FALSE); + } + case 12: + if(prevdog != mode) { + prevdog = mode; + nodogs++; + CreateVariable(data,nodogs,3,0.0,"Invariants",FALSE); + } + if(secno==1 && argno == 3) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno-2])); + if(secno==1 && argno == 4) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno-1])); + if(secno==1 && argno == 5) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno])); + break; + + /* Read vectors in nodes. */ + case 101: + if(prevdog != mode) { + prevdog = mode; + nodogs++; + CreateVariable(data,nodogs,3,0.0,"Displacement",FALSE); + } + case 102: + if(prevdog != mode) { + prevdog = mode; + nodogs++; + CreateVariable(data,nodogs,3,0.0,"Velocity",FALSE); + } + if(argno == 3) knotno = indx[atoi(buffer)]; + if(argno == 4) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno-2])); + if(argno == 5) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno-1])); + if(argno == 6) sscanf(buffer,"%le",&(data->dofs[nodogs][3*knotno])); + break; + + default: + if(ignored != mode) { + printf("Record %d was ignored!\n",mode); + ignored = mode; + } + break; + } + } + +jump: + + if(info) printf("Renumbering elements\n"); + for(j=1;j<=data->noelements;j++) + for(i=0;i < data->elementtypes[j]%100;i++) + data->topology[j][i] = indx[data->topology[j][i]]; + + free_ivector(indx,0,2*data->noknots); + + fclose(in); + + if(info) printf("LoadAbacusInput: results were loaded from file %s.\n",filename); + + return(0); +} + + int LoadNastranInput(struct FemType *data,struct BoundaryType *bound, @@ -558,22 +1091,19 @@ int LoadNastranInput(struct FemType *data,struct BoundaryType *bound, /* Load the grid from a format that in Nastran format */ { - int noknots,noelements,elemcode,maxnodes,material; - int mode,allocated,maxknot,minknot; - int boundarytype,boundarynodes,nodes; - + int noknots,noelements,maxnodes; + int allocated,maxknot,minknot,nodes; char filename[MAXFILESIZE]; char line[MAXLINESIZE],*cp; - int j,k=0; + int j,k; FILE *in; - int ivalues0[MAXDOFS]; strcpy(filename,prefix); if ((in = fopen(filename,"r")) == NULL) { AddExtension(prefix,filename,"nas"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadNastranInput: opening of the Nastran file '%s' wasn't succesfull !\n", + printf("LoadNastranInput: opening of the Nastran file '%s' wasn't successful !\n", filename); return(1); } @@ -591,19 +1121,12 @@ int LoadNastranInput(struct FemType *data,struct BoundaryType *bound, second time. */ omstart: - mode = 0; maxnodes = 0; noknots = 0; noelements = 0; - elemcode = 0; - boundarytype = 0; - boundarynodes = 0; - material = 0; - ivalues0[0] = ivalues0[1] = 0; - for(;;) { - /* getline; */ + /* GETLINE; */ if (Getrow(line,in,TRUE)) goto end; @@ -732,14 +1255,11 @@ int LoadNastranInput(struct FemType *data,struct BoundaryType *bound, static void ReorderFidapNodes(struct FemType *data,int element,int nodes,int typeflag) { int i,oldtopology[MAXNODESD2],*topology,dim; - int order203[]={1,3,2}; - int order306[]={1,3,5,2,4,6}; + int order808[]={1,2,4,3,5,6,8,7}; int order408[]={1,3,5,7,2,4,6,8}; - int order409[]={1,3,5,7,2,4,6,8,9}; - int order510[]={1,3,6,10,2,5,4,7,8,9}; + int order306[]={1,3,5,2,4,6}; + int order203[]={1,3,2}; int order605[]={1,2,4,3,5}; - int order808[]={1,2,4,3,5,6,8,7}; - int order827[]={1,3,9,7,19,21,27,25,2,6,8,4,10,12,18,16,20,24,26,22,11,15,17,13,5,23,14}; dim = data->dim; if(typeflag > 10) dim -= 1; @@ -771,25 +1291,11 @@ static void ReorderFidapNodes(struct FemType *data,int element,int nodes,int typ for(i=0;ielementtypes[element] = 409; - for(i=0;ielementtypes[element] = 504; } - else if(nodes == 10) { - data->elementtypes[element] = 510; - for(i=0;ielementtypes[element] = 605; for(i=0;ielementtypes[element] = 808; - } - else if(nodes == 27) { - for(i=0;ielementtypes[element] = 827; } else { printf("Unknown Fidap elementtype with %d nodes.\n",nodes); @@ -820,7 +1318,6 @@ static void ReorderFidapNodes(struct FemType *data,int element,int nodes,int typ } else printf("ReorderFidapNodes: unknown dimension (%d)\n",data->dim); - if(0) printf("dim = %d element = %d elemtype = %d\n",dim,element,data->elementtypes[element]); } @@ -833,7 +1330,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr Still under implementation */ { - int noknots,noelements,dim,novel,elemcode,maxnodes; + int noknots,noelements,dim,novel,maxnodes; int mode,maxknot,totelems,entity,maxentity; char filename[MAXFILESIZE]; char line[MAXLINESIZE],entityname[MAXNAMESIZE]; @@ -849,7 +1346,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr if ((in = fopen(filename,"r")) == NULL) { AddExtension(prefix,filename,"FDNEUT"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadFidapInput: opening of the Fidap-file '%s' wasn't succesfull !\n", + printf("LoadFidapInput: opening of the Fidap-file '%s' wasn't successful !\n", filename); return(1); } @@ -862,17 +1359,14 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr noknots = 0; noelements = 0; dim = 0; - elemcode = 0; maxnodes = 4; totelems = 0; maxentity = 0; for(;;) { - isio = getline; + isio = GETLINE; if(!isio) goto end; - if(!line) goto end; - if(line=="") goto end; if(strstr(line,"END")) goto end; /* Control information */ @@ -892,14 +1386,14 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr case 1: if(info) printf("Loading FIDAP input file %s\n",filename); - getline; + GETLINE; if(info) printf("Name of the case: %s",line); mode = 0; break; case 2: - getline; - if(0) printf("Reading the header info\n"); + GETLINE; + if(0) printf("reading the header info\n"); sscanf(line,"%d%d%d%d%d",&noknots,&noelements, &nogroups,&dim,&novel); data->noknots = noknots; @@ -916,7 +1410,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr AllocateKnots(data); if(info) printf("Reading the nodes\n"); for(i=1;i<=noknots;i++) { - getline; + GETLINE; if (dim == 2) sscanf(line,"%d%le%le",&knotno, &(data->x[i]),&(data->y[i])); @@ -952,7 +1446,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr while(val!=':');i++; sscanf(&line[i],"%d",&typeflag); - getline; + GETLINE; i=0; do val=line[i++]; while(val!=':');i++; @@ -971,22 +1465,30 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr data->topology = topology; } - if(0) printf("Reading %d element topologies with %d nodes for %s\n", + if(info) printf("reading %d element topologies with %d nodes for %s\n", elems,nodes,entityname); for(entity=1;entity<=maxentity;entity++) { +#if 0 + k = strcmp(entityname,entitylist[entity]); +#else k = strcmp(entityname,data->bodyname[entity]); +#endif if(k == 0) break; } if(entity > maxentity) { maxentity++; +#if 0 + strcpy(entitylist[entity],entityname); +#else strcpy(data->bodyname[entity],entityname); +#endif if(info) printf("Found new entity: %s\n",entityname); } for(i=totelems+1;i<=totelems+elems;i++) { - getline; + GETLINE; cp = line; j = next_int(&cp); @@ -1013,7 +1515,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr CreateVariable(data,2,dim,0.0,"Velocity",FALSE); vel = data->dofs[2]; for(j=1;j<=noknots;j++) { - getline; + GETLINE; if(dim==2) sscanf(line,"%le%le",&(vel[2*j-1]),&(vel[2*j])); if(dim==3) @@ -1027,7 +1529,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr CreateVariable(data,1,1,0.0,"Temperature",FALSE); temp = data->dofs[1]; for(j=1;j<=noknots;j++) { - getline; + GETLINE; sscanf(line,"%le",&(temp[j])); } mode = 0; @@ -1074,9 +1576,8 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr if(info) printf("Finished reading the Fidap neutral file\n"); - ElementsToBoundaryConditions(data,boundaries,FALSE,TRUE); - RenumberBoundaryTypes(data,boundaries,TRUE,0,info); + /* RenumberBoundaryTypes(data,boundaries,TRUE,0,info); */ return(0); } @@ -1086,21 +1587,32 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr static void ReorderAnsysNodes(struct FemType *data,int *oldtopology, int element,int dim,int nodes) { - int i,*topology,elementtype = 0; + int i,*topology,elementtype; int order820[]={1,2,3,4,5,6,7,8,9,10,11,12,17,18,19,20,13,14,15,16}; int order504[]={1,2,3,5}; int order306[]={1,2,3,5,6,8}; int order510[]={1,2,3,5,9,10,12,17,18,19}; int order613[]={1,2,3,4,5,9,10,11,12,17,18,19,20}; - + int order706[]={1,2,3,5,6,7}; + int order715[]={1,2,3,5,6,7,9,10,12,17,18,19,13,14,16}; + + elementtype = 0; if(dim == 3) { if(nodes == 20) { - if(oldtopology[2] == oldtopology[3]) elementtype = 510; + if(oldtopology[2] == oldtopology[3] && + oldtopology[4] == oldtopology[5]) elementtype = 510; + else if(oldtopology[2] == oldtopology[3] && + oldtopology[4] != oldtopology[5]) elementtype = 715; else if(oldtopology[4] == oldtopology[5]) elementtype = 613; else elementtype = 820; } if(nodes == 8) { - if(oldtopology[2] == oldtopology[3]) elementtype = 504; + if(oldtopology[2] == oldtopology[3] && + oldtopology[4] == oldtopology[7] && + oldtopology[5] == oldtopology[7] && + oldtopology[6] == oldtopology[7]) elementtype = 504; + else if(oldtopology[2] == oldtopology[3] && + oldtopology[6] == oldtopology[7]) elementtype = 706; else if(oldtopology[4] == oldtopology[5]) elementtype = 605; else elementtype = 808; } @@ -1142,6 +1654,10 @@ static void ReorderAnsysNodes(struct FemType *data,int *oldtopology, break; case 504: + if(nodes == 4) + for(i=0;i 8) { - getline; + GETLINE; cp=line; if(ansysnodes[k] == 10 && topology[2] != topology[3]) @@ -1380,12 +1916,20 @@ int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound, imax = 20; for(i=8;ibodynamesexist = TRUE; if(bound[0].nosides) { @@ -1462,7 +2006,7 @@ int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound, sscanf(line,"%d%s%s%d",&bcind,&text[0],&text2[0],&sides); if(strstr(text2,"BODY")) { - getline; + GETLINE; sscanf(line,"%d%d",&j,&bcind); strcpy(data->bodyname[bcind],text); } @@ -1471,12 +2015,12 @@ int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound, for(i=1;i<=maxside;i++) bctypes[i] = 0; for(i=1;i<=sides;i++) { - getline; + GETLINE; sscanf(line,"%d%d",&j,&bcind); bctypes[bcind] = TRUE; } - /* Find 1st unsed boundarytype */ + /* Find 1st unused boundarytype */ for(i=1;i<=maxside;i++) if(bctypes[i] && !bctypeused[i]) break; @@ -1532,20 +2076,21 @@ int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound, free_Ivector(boundindx,1,boundarynodes); free_Ivector(nodeindx,1,boundarynodes); + if(info) printf("Ansys mesh loaded succefully\n"); + return(0); } - - static void ReorderFieldviewNodes(struct FemType *data,int *oldtopology, int element,int dim,int nodes) { - int i,*topology,elementtype = 0; + int i,*topology,elementtype; int order808[]={1,2,4,3,5,6,8,7}; int order706[]={1,4,6,2,3,5}; int order404[]={1,2,3,4}; + elementtype = 0; if(dim == 3) { if(nodes == 8) elementtype = 808; if(nodes == 6) elementtype = 706; @@ -1559,6 +2104,7 @@ static void ReorderFieldviewNodes(struct FemType *data,int *oldtopology, if(!elementtype) { printf("Unknown elementtype in element %d (%d nodes, %d dim).\n", element,nodes,dim); + bigerror("Cannot continue"); } data->elementtypes[element] = elementtype; @@ -1580,21 +2126,21 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre program by PointWise. This is a suitable format to read files created by GridGen. */ { - int noknots,noelements,elemcode,maxnodes; - int mode,totelems,entity; + int noknots,noelements,maxnodes,mode; char filename[MAXFILESIZE]; char line[MAXLINESIZE],*cp; int i,j,k; FILE *in; Real x,y,z; - int maxindx,sidenodes; + int maxindx; char *isio; - int nobound,nobulk = 0,maxsidenodes,*boundtypes = NULL,**boundtopos = NULL,*boundnodes = NULL,*origtopology; + int nobound=0,nobulk=0,maxsidenodes; + int *boundtypes=NULL,**boundtopos=NULL,*boundnodes=NULL,*origtopology=NULL; if ((in = fopen(prefix,"r")) == NULL) { AddExtension(prefix,filename,"dat"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadFieldviewInput: opening of the Fieldview-file '%s' wasn't succesfull !\n", + printf("LoadFieldviewInput: opening of the Fieldview-file '%s' wasn't successful !\n", filename); return(1); } @@ -1604,29 +2150,21 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre data->dim = 3; data->created = TRUE; - entity = 0; mode = 0; noknots = 0; noelements = 0; - elemcode = 0; - maxnodes = 8; maxsidenodes = 4; maxindx = 0; - sidenodes = 0; data->maxnodes = maxnodes; - totelems = 0; - for(;;) { if(mode == 0) { - isio = getline; + isio = GETLINE; if(!isio) goto end; - if(!line) goto end; - if(line=="") goto end; if(strstr(line,"END")) goto end; /* Control information */ @@ -1671,7 +2209,7 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre case 6: - getline; + GETLINE; sscanf(line,"%d",&noknots); data->noknots = noknots; @@ -1682,7 +2220,7 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre data->z = Rvector(1,noknots); for(i=1;i<=noknots;i++) { - getline; + GETLINE; sscanf(line,"%le%le%le",&x,&y,&z); data->x[i] = x; data->y[i] = y; @@ -1693,7 +2231,7 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre case 7: - getline; + GETLINE; sscanf(line,"%d",&nobound); if(info) printf("Loading %d boundary element definitions\n",nobound); @@ -1703,7 +2241,7 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre boundnodes = Ivector(1,nobound); for(i=1;i<=nobound;i++) { - getline; cp=line; + GETLINE; cp=line; boundtypes[i]= next_int(&cp); maxsidenodes = next_int(&cp); @@ -1730,7 +2268,7 @@ int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *pre data->elementtypes = Ivector(1,noelements); for(i=0;;) { - getline; cp=line; + GETLINE; cp=line; if(strstr(line,"Variables")) mode = 9; if(mode != 8) break; @@ -1810,19 +2348,20 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, FILE *in; char *cp,line[MAXLINESIZE],elemfile[MAXFILESIZE],nodefile[MAXFILESIZE], polyfile[MAXLINESIZE]; + int *invrow,*invcol; if(info) printf("Loading mesh in Triangle format from file %s.*\n",prefix); sprintf(nodefile,"%s.node",prefix); if ((in = fopen(nodefile,"r")) == NULL) { - printf("LoadElmerInput: The opening of the nodes file %s failed!\n",nodefile); + printf("LoadTriangleInput: The opening of the nodes file %s failed!\n",nodefile); return(1); } else printf("Loading nodes from file %s\n",nodefile); - getline; + GETLINE; sscanf(line,"%d %d %d %d",&noknots,&dim,&nodeatts,&bcmarkers); fclose(in); @@ -1833,13 +2372,13 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, sprintf(elemfile,"%s.ele",prefix); if ((in = fopen(elemfile,"r")) == NULL) { - printf("LoadElmerInput: The opening of the element file %s failed!\n",elemfile); + printf("LoadTriangleInput: The opening of the element file %s failed!\n",elemfile); return(3); } else printf("Loading elements from file %s\n",elemfile); - getline; + GETLINE; sscanf(line,"%d %d %d",&noelements,&maxnodes,&elematts); fclose(in); @@ -1859,9 +2398,9 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, boundnodes[i] = 0; in = fopen(nodefile,"r"); - getline; + GETLINE; for(i=1; i <= noknots; i++) { - getline; + GETLINE; cp = line; j = next_int(&cp); if(j != i) printf("LoadTriangleInput: nodes i=%d j=%d\n",i,j); @@ -1876,9 +2415,9 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, fclose(in); in = fopen(elemfile,"r"); - getline; + GETLINE; for(i=1; i <= noelements; i++) { - getline; + GETLINE; cp = line; data->elementtypes[i] = elementtype; j = next_int(&cp); @@ -1897,36 +2436,40 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, sprintf(polyfile,"%s.poly",prefix); if ((in = fopen(polyfile,"r")) == NULL) { - printf("LoadElmerInput: The opening of the poly file %s failed!\n",polyfile); + printf("LoadTriangleInput: The opening of the poly file %s failed!\n",polyfile); return(1); } else printf("Loading nodes from file %s\n",polyfile); { - int bcelems,markers,ind1,ind2,bctype,j2,k2,hit = 0; - int elemsides,sideind[2],side,elemind = 0; + int bcelems,markers,ind1,ind2,bctype,j2,k2,hit; + int elemsides,sideind[2],side,elemind=0; bctype = 1; elemsides = 3; + hit = FALSE; - getline; - getline; + GETLINE; + GETLINE; sscanf(line,"%d %d",&bcelems,&markers); CreateInverseTopology(data,info); + invrow = data->invtopo.rows; + invcol = data->invtopo.cols; AllocateBoundary(bound,bcelems); for(i=1;i<=bcelems;i++) { - getline; + GETLINE; if(markers) sscanf(line,"%d %d %d %d",&j,&ind1,&ind2,&bctype); else sscanf(line,"%d %d %d",&j,&ind1,&ind2); /* find an element which owns both the nodes */ +#if 0 for(j=1;j<=data->maxinvtopo;j++) { hit = FALSE; k = data->invtopo[j][ind1]; @@ -1943,6 +2486,24 @@ int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound, } if(hit) break; } +#else + for(j=invrow[ind1-1];jelementtypes[i] = elementtype; @@ -2096,16 +2656,16 @@ int LoadGidInput(struct FemType *data,struct BoundaryType *bound, char *prefix,int info) /* Load the grid from GID mesh format */ { - int noknots,noelements,elemcode,maxnodes,material,foundsame; - int mode,allocated,maxknot,nosides,sideelemtype; - int boundarytype,materialtype,boundarynodes,side,parent,elemsides; - int dim = 0, elemnodes = 0, elembasis = 0, elemtype = 0, bulkdone, usedmax = 0,hits; + int noknots,noelements,maxnodes,foundsame; + int mode,allocated,nosides,sideelemtype; + int boundarytype,side,parent,elemsides,materialtype=0; + int dim=0, elemnodes=0, elembasis=0, elemtype=0, bulkdone, usedmax=0,hits; int minbulk,maxbulk,minbound,maxbound,label,debug; - int *usedno = NULL, **usedelem = NULL; + int *usedno=NULL, **usedelem=NULL; char filename[MAXFILESIZE],line[MAXLINESIZE],*cp; int i,j,k,n,ind,inds[MAXNODESD2],sideind[MAXNODESD1]; FILE *in; - Real x,y,z = 0; + Real x,y,z; debug = FALSE; @@ -2113,7 +2673,7 @@ int LoadGidInput(struct FemType *data,struct BoundaryType *bound, if ((in = fopen(filename,"r")) == NULL) { AddExtension(prefix,filename,"msh"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadAbaqusInput: opening of the GID-file '%s' wasn't succesfull !\n", + printf("LoadGidInput: opening of the GID-file '%s' wasn't successful !\n", filename); return(1); } @@ -2123,7 +2683,6 @@ int LoadGidInput(struct FemType *data,struct BoundaryType *bound, InitializeKnots(data); allocated = FALSE; - maxknot = 0; /* Because the file format doesn't provide the number of elements or nodes the results are read twice but registered only in the @@ -2138,17 +2697,13 @@ int LoadGidInput(struct FemType *data,struct BoundaryType *bound, maxnodes = 0; noknots = 0; noelements = 0; - elemcode = 0; boundarytype = 0; - boundarynodes = 0; - material = 0; nosides = 0; bulkdone = FALSE; for(;;) { if(Getrow(line,in,FALSE)) goto end; - if(!line) goto end; if(strstr(line,"MESH")) { if(debug) printf("MESH\n"); @@ -2426,7 +2981,8 @@ static void ReorderComsolNodes(int elementtype,int *topo) int i,tmptopo[MAXNODESD2]; int order404[]={1,2,4,3}; int order808[]={1,2,4,3,5,6,8,7}; - + int order605[]={1,2,4,3,5}; + switch (elementtype) { @@ -2444,6 +3000,14 @@ static void ReorderComsolNodes(int elementtype,int *topo) topo[i] = tmptopo[order808[i]-1]; break; + case 605: + for(i=0;i maxnodes) maxnodes = elemnodes; if(debug) printf("elemnodes=%d\n",elemnodes); } - else if(strstr(line,"# Mesh point coordinates")) { + else if(strstr(line,"# Mesh point coordinates") || strstr(line, "# Mesh vertex coordinates" )) { printf("Loading %d coordinates\n",noknots); for(i=1;i<=noknots;i++) { @@ -2630,7 +3195,7 @@ int LoadComsolMesh(struct FemType *data,struct BoundaryType *bound,char *prefix, if(!allocated) { if(noknots == 0 || noelements == 0 || maxnodes == 0) { - printf("Invalid mesh consits of %d knots and %d %d-node elements.\n", + printf("Invalid mesh consists of %d knots and %d %d-node elements.\n", noknots,noelements,maxnodes); fclose(in); return(2); @@ -2660,6 +3225,7 @@ int LoadComsolMesh(struct FemType *data,struct BoundaryType *bound,char *prefix, } + static int GmshToElmerType(int gmshtype) { int elmertype = 0; @@ -2705,7 +3271,6 @@ static int GmshToElmerType(int gmshtype) case 15: elmertype = 101; break; - case 16: elmertype = 408; break; @@ -2722,6 +3287,38 @@ static int GmshToElmerType(int gmshtype) elmertype = 310; break; + /* These are supported by Gmsh but not by ElmerSolver */ + case 13: + elmertype = 718; + break; + case 14: + elmertype = 614; + break; + case 20: + elmertype = 309; + break; + case 22: + elmertype = 312; + break; + case 24: + elmertype = 315; + break; + case 25: + elmertype = 320; + break; + case 26: + elmertype = 204; + break; + case 27: + elmertype = 205; + break; + case 28: + elmertype = 206; + break; + case 29: + elmertype = 520; + break; + default: printf("Gmsh element %d does not have an Elmer counterpart!\n",gmshtype); } @@ -2736,10 +3333,12 @@ static void GmshToElmerIndx(int elemtype,int *topology) int reorder, *porder; int order510[]={0,1,2,3,4,5,6,7,9,8}; - int order613[]={0,1,2,3,4,5,8,10,6,7,9,11,12}; - int order715[]={0,1,2,3,4,5,6,9,7,8,10,11,12,14,13}; - int order820[]={0,1,2,3,4,5,6,7,8,11,12,9,10,12,14,15,16,18,19,17}; - + int order614[]={0,1,2,3,4,5,8,10,6,7,9,11,12,13}; + int order718[]={0,1,2,3,4,5,6,9,7,8,10,11,12,14,13,15,17,16}; + int order820[]={0,1,2,3,4,5,6,7,8,11,13,9,10,12,14,15,16,18,19,17}; + int order827[]={0,1,2,3,4,5,6,7,8,11,13,9,10,12,14,15,16,18,19,17,21,23,24,22,20,25,26}; + /* {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26}; */ + reorder = FALSE; @@ -2751,20 +3350,26 @@ static void GmshToElmerIndx(int elemtype,int *topology) break; case 613: + case 614: reorder = TRUE; - porder = &order613[0]; + porder = &order614[0]; break; case 715: + case 718: reorder = TRUE; - porder = &order715[0]; + porder = &order718[0]; break; case 820: reorder = TRUE; porder = &order820[0]; break; - + + case 827: + reorder = TRUE; + porder = &order827[0]; + break; } if( reorder ) { @@ -2780,22 +3385,22 @@ static void GmshToElmerIndx(int elemtype,int *topology) static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, char *filename,int info) { - int noknots = 0,noelements = 0,maxnodes,elematts,nodeatts,nosides,dim; - int sideind[MAXNODESD1],elemind[MAXNODESD2],tottypes,elementtype,bcmarkers; - int i,j,k,dummyint,*boundnodes,allocated,*revindx,maxindx; - int elemno, gmshtype, regphys, regelem, elemnodes,maxelemtype,elemdim; + int noknots = 0,noelements = 0,maxnodes,dim; + int elemind[MAXNODESD2],elementtype; + int i,j,k,allocated,*revindx=NULL,maxindx; + int elemno, gmshtype, regphys, regelem, elemnodes,maxelemtype; FILE *in; char *cp,line[MAXLINESIZE]; if ((in = fopen(filename,"r")) == NULL) { - printf("LoadElmerInput: The opening of the mesh file %s failed!\n",filename); + printf("LoadGmshInput: The opening of the mesh file %s failed!\n",filename); return(1); } if(info) printf("Loading mesh in Gmsh format 1.0 from file %s\n",filename); allocated = FALSE; - dim = 3; + dim = data->dim; maxnodes = 0; maxindx = 0; maxelemtype = 0; @@ -2822,17 +3427,16 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, for(;;) { if(Getrow(line,in,TRUE)) goto end; - if(!line) goto end; if(strstr(line,"END")) goto end; if(strstr(line,"$NOD")) { - getline; + GETLINE; cp = line; noknots = next_int(&cp); for(i=1; i <= noknots; i++) { - getline; + GETLINE; cp = line; j = next_int(&cp); @@ -2846,7 +3450,7 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, maxindx = MAX(j,maxindx); } } - getline; + GETLINE; if(!strstr(line,"$ENDNOD")) { printf("NOD section should end to string ENDNOD\n"); printf("%s\n",line); @@ -2854,12 +3458,12 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, } if(strstr(line,"$ELM")) { - getline; + GETLINE; cp = line; noelements = next_int(&cp); for(i=1; i <= noelements; i++) { - getline; + GETLINE; cp = line; elemno = next_int(&cp); @@ -2891,7 +3495,7 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, } } - getline; + GETLINE; if(!strstr(line,"$ENDELM")) printf("ELM section should end to string ENDELM\n"); } @@ -2920,7 +3524,7 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, if(k <= 0 || k > maxindx) printf("index out of bounds %d\n",k); else if(revindx[k] <= 0) - printf("unkonwn node %d %d in element %d\n",k,revindx[k],i); + printf("unknown node %d %d in element %d\n",k,revindx[k],i); else data->topology[i][j] = revindx[k]; } @@ -2937,39 +3541,41 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, - char *filename,int info) + char *filename,int usetaggeom, int info) { - int noknots = 0,noelements = 0,maxnodes,elematts,nodeatts,nosides,dim,notags; - int sideind[MAXNODESD1],elemind[MAXNODESD2],tottypes,elementtype,bcmarkers; - int i,j,k,dummyint,*boundnodes,allocated,*revindx,maxindx; - int elemno, gmshtype, tagphys, taggeom, tagpart, elemnodes,maxelemtype,elemdim; - int usetaggeom,tagmat,verno; + int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; + int elemind[MAXNODESD2],elementtype; + int i,j,k,allocated,*revindx=NULL,maxindx; + int elemno, gmshtype, tagphys=0, taggeom=0, tagpart, elemnodes,maxelemtype; + int tagmat,verno; + int physvolexist, physsurfexist; FILE *in; + const char manifoldname[4][10] = {"point", "line", "surface", "volume"}; char *cp,line[MAXLINESIZE]; - if ((in = fopen(filename,"r")) == NULL) { - printf("LoadElmerInput: The opening of the mesh file %s failed!\n",filename); + printf("LoadGmshInput2: The opening of the mesh file %s failed!\n",filename); return(1); } if(info) printf("Loading mesh in Gmsh format 2.0 from file %s\n",filename); allocated = FALSE; - dim = 3; + dim = data->dim; maxnodes = 0; maxindx = 0; maxelemtype = 0; + physvolexist = FALSE; + physsurfexist = FALSE; usetaggeom = FALSE; - + omstart: for(;;) { if(Getrow(line,in,FALSE)) goto end; - if(!line) goto end; if(strstr(line,"$End")) continue; if(strstr(line,"$MeshFormat")) { - getline; + GETLINE; cp = line; verno = next_int(&cp); @@ -2977,19 +3583,19 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, printf("Version number is not compatible with the parser: %d\n",verno); } - getline; + GETLINE; if(!strstr(line,"$EndMeshFormat")) { printf("$MeshFormat section should end to string $EndMeshFormat:\n%s\n",line); } } else if(strstr(line,"$Nodes")) { - getline; + GETLINE; cp = line; noknots = next_int(&cp); for(i=1; i <= noknots; i++) { - getline; + GETLINE; cp = line; j = next_int(&cp); @@ -3003,19 +3609,19 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, maxindx = MAX(j,maxindx); } } - getline; + GETLINE; if(!strstr(line,"$EndNodes")) { printf("$Nodes section should end to string $EndNodes:\n%s\n",line); } } else if(strstr(line,"$Elements")) { - getline; + GETLINE; cp = line; noelements = next_int(&cp); for(i=1; i <= noelements; i++) { - getline; + GETLINE; cp = line; elemno = next_int(&cp); @@ -3056,19 +3662,37 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, } } - getline; + GETLINE; if(!strstr(line,"$EndElements")) { printf("$Elements section should end to string $EndElements:\n%s\n",line); } } else if(strstr(line,"$PhysicalNames")) { - if(info) printf("Physical names are not accounted for\n"); - getline; + GETLINE; cp = line; - i = next_int(&cp); - for(;i>0;i--) getline; + nophysical = next_int(&cp); + for(i=0;iboundaryname[tagphys]); + else printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim-1],cp+1); + } + else if(gmshtype == dim) { + physvolexist = TRUE; + if(tagphys < MAXBODIES) sscanf(cp," \"%[^\"]\"",data->bodyname[tagphys]); + else printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim],cp+1); + } + else printf("Physical groups of dimension %d not supported in %d-dimensional mesh: " + "ignoring group %d %s",gmshtype,dim,tagphys,cp+1); + } + } - getline; + GETLINE; if(!strstr(line,"$EndPhysicalNames")) { printf("$PhysicalNames section should end to string $EndPhysicalNames:\n%s\n",line); } @@ -3114,7 +3738,7 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, if(k <= 0 || k > maxindx) printf("index out of bounds %d\n",k); else if(revindx[k] <= 0) - printf("unkonwn node %d %d in element %d\n",k,revindx[k],i); + printf("unknown node %d %d in element %d\n",k,revindx[k],i); else data->topology[i][j] = revindx[k]; } @@ -3124,11 +3748,8 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, ElementsToBoundaryConditions(data,bound,FALSE,info); - /* The geometric entities are rather randomly numbered */ - if( usetaggeom ) { - RenumberBoundaryTypes(data,bound,TRUE,0,info); - RenumberMaterialTypes(data,bound,info); - } + data->bodynamesexist = physvolexist; + data->boundarynamesexist = physsurfexist; if(info) printf("Successfully read the mesh from the Gmsh input file.\n"); @@ -3137,17 +3758,17 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, - char *filename,int info) + char *filename,int usetaggeom, int info) { int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; int elemind[MAXNODESD2],elementtype; - int i,j,k,allocated,*revindx=NULL,maxindx; - int elemno, gmshtype, tagphys=0, taggeom=0, tagpart, elemnodes,maxelemtype; - int usetaggeom,tagmat,verno; - int physvolexist, physsurfexist; + int i,j,k,l,allocated,*revindx=NULL,maxindx; + int elemno, gmshtype, tagphys=0, tagpart, elemnodes,maxelemtype; + int tagmat,verno; + int physvolexist, physsurfexist,**tagmap,tagsize,maxtag[4]; FILE *in; const char manifoldname[4][10] = {"point", "line", "surface", "volume"}; - char *cp,line[MAXLINESIZE]; + char *cp,line[MAXLINESIZE],longline[LONGLINESIZE]; if ((in = fopen(filename,"r")) == NULL) { printf("LoadGmshInput4: The opening of the mesh file %s failed!\n",filename); @@ -3160,10 +3781,12 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, maxnodes = 0; maxindx = 0; maxelemtype = 0; - usetaggeom = FALSE; physvolexist = FALSE; physsurfexist = FALSE; + usetaggeom = TRUE; /* The default */ + for(i=0;i<4;i++) maxtag[i] = 0; + omstart: for(;;) { @@ -3171,7 +3794,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, if(strstr(line,"$End")) continue; if(strstr(line,"$MeshFormat")) { - getline; + GETLINE; cp = line; verno = next_int(&cp); @@ -3179,7 +3802,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, printf("Version number is not compatible with the parser: %d\n",verno); } - getline; + GETLINE; if(!strstr(line,"$EndMeshFormat")) { printf("$MeshFormat section should end to string $EndMeshFormat:\n%s\n",line); } @@ -3188,7 +3811,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, else if(strstr(line,"$Nodes")) { int numEntityBlocks,tagEntity,dimEntity,parEntity,numNodes,ind; - getline; + GETLINE; cp = line; numEntityBlocks = next_int(&cp); @@ -3199,7 +3822,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, k = 0; for(j=1; j <= numEntityBlocks; j++) { - getline; + GETLINE; cp = line; tagEntity = next_int(&cp); @@ -3208,7 +3831,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, numNodes = next_int(&cp); for(i=1; i <= numNodes; i++) { - getline; + GETLINE; cp = line; k += 1; @@ -3224,7 +3847,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, } } } - getline; + GETLINE; if(!strstr(line,"$EndNodes")) { printf("$Nodes section should end to string $EndNodes:\n%s\n",line); @@ -3232,45 +3855,109 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, } else if(strstr(line,"$Entities")) { - int numPoints, numCurves, numSurfaces, numVolumes; + int numPoints, numCurves, numSurfaces, numVolumes, numEnt; + int tag,tagdim,nophys,phystag; + int nobound, idum; + Real rdum; - getline; + usetaggeom = FALSE; + + GETLINE; cp = line; numPoints = next_int(&cp); numCurves = next_int(&cp); numSurfaces = next_int(&cp); numVolumes = next_int(&cp); - if(allocated && info) printf("num entities: %d %d %d %d\n",numPoints,numCurves,numSurfaces,numVolumes); - - for(i=1; i <= numPoints; i++) { - getline; - cp = line; - } - for(i=1; i <= numCurves; i++) { - getline; - cp = line; + if(allocated) { + tagsize = 0; + for(tagdim=0;tagdim<=3;tagdim++) + tagsize = MAX( tagsize, maxtag[tagdim]); + if( tagsize > 0 ) { + tagmap = Imatrix(0,3,1,tagsize); + for(i=0;i<=3;i++) + for(j=1;j<=tagsize;j++) + tagmap[i][j] = 0; + } } + + for(tagdim=0;tagdim<=3;tagdim++) { + + if( tagdim == 0 ) + numEnt = numPoints; + else if( tagdim == 1 ) + numEnt = numCurves; + else if( tagdim == 2 ) + numEnt = numSurfaces; + else if( tagdim == 3 ) + numEnt = numVolumes; + + if(!allocated) + maxtag[tagdim] = 0; + else if( maxtag[tagdim] > 0 ) + printf("Maximum original tag for %d %dDIM entities is %d\n",numEnt,tagdim,maxtag[tagdim]); - for(i=1; i <= numSurfaces; i++) { - getline; - cp = line; - } - for(i=1; i <= numVolumes; i++) { - getline; - cp = line; + if(numEnt > 0 && !allocated) { + printf("Reading %d entities in %dD\n",numEnt,tagdim); + } + + for(i=1; i <= numEnt; i++) { + GETLONGLINE; + + // if( i==1 ) printf("1st line of dim %d with %d entries: %s\n",tagdim,numEnt,line); + + if( tagdim == 0 ) continue; + + cp = longline; + tag = next_int(&cp); + + if(!allocated) + maxtag[tagdim] = MAX( maxtag[tagdim], tag ); + + for(j=1;j<=6;j++) rdum = next_real(&cp); + nophys = next_int(&cp); + + if( nophys > 0 ) phystag = next_int(&cp); + + if(allocated) tagmap[tagdim][tag] = phystag; + + + // The lines may be too long. So fill the string buffer until we get a newline. + j = k = 0; + for(;;) { + for(l=0;l 0 && !allocated) printf("Entity line %d has length %d.\n",i,k+j); + + //for(j=2;j<=nophys;j++) + // idum = next_int(&cp); + + //// if( tagdim == 0 ) continue; + + //nobound = next_int(&cp); + // for(j=1;j<=nobound;j++) + // idum = next_int(&cp); + } } - - getline; - if(!strstr(line,"$EndEntities")) { - printf("$Entities section should end to string $EndEntities:\n%s\n",line); + + GETLONGLINE; + if(!strstr(longline,"$EndEntities")) { + printf("$Entities section should end to string $EndEntities:\n%s\n",longline); } } else if(strstr(line,"$Elements")) { int numEntityBlocks, numElements, tagEntity, dimEntity, typeEle, NumElements; - getline; + GETLINE; cp = line; k = 0; @@ -3282,72 +3969,94 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, for(j=1; j<= numEntityBlocks; j++ ) { - getline; + GETLINE; cp = line; tagEntity = next_int(&cp); dimEntity = next_int(&cp); + typeEle = next_int(&cp); numElements = next_int(&cp); - + elementtype = GmshToElmerType(typeEle); elemnodes = elementtype % 100; maxelemtype = MAX(maxelemtype,elementtype); + if( allocated && tagsize > 0 ) { + printf("Reading %d elements with tag %d of type %d\n", numElements, tagEntity, elementtype); + if( tagsize > 0 ) { + if( tagmap[dimEntity][tagEntity] ) { + printf("Mapping mesh tag %d to physical tag %d in %dDIM\n",tagEntity,tagmap[dimEntity][tagEntity],dimEntity); + tagEntity = tagmap[dimEntity][tagEntity]; + } + else { + printf("Mesh tag %d is not associated to any physical tag!\n",tagEntity); + } + } + } + for(i=1; i <= numElements; i++) { - getline; + GETLINE; cp = line; k += 1; elemno = next_int(&cp); - + if(allocated) { data->elementtypes[k] = elementtype; data->material[k] = tagEntity; - for(j=0;jtopology[k][j] = elemind[j]; + for(l=0;ltopology[k][l] = elemind[l]; } } } - getline; + GETLINE; if(!strstr(line,"$EndElements")) { printf("$Elements section should end to string $EndElements:\n%s\n",line); } } else if(strstr(line,"$PhysicalNames")) { - getline; + GETLINE; cp = line; nophysical = next_int(&cp); for(i=0;iboundaryname[tagphys]); - else printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim-1],cp+1); + if(tagphys < MAXBCS) { + sscanf(cp," \"%[^\"]\"",data->boundaryname[tagphys]); + printf("Boundary name for physical group %d is: %s\n",tagphys,data->boundaryname[tagphys]); + } + else + printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim-1],cp+1); } else if(gmshtype == dim) { physvolexist = TRUE; - if(tagphys < MAXBODIES) sscanf(cp," \"%[^\"]\"",data->bodyname[tagphys]); - else printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim],cp+1); + if(tagphys < MAXBODIES) { + sscanf(cp," \"%[^\"]\"",data->bodyname[tagphys]); + printf("Body name for physical group %d is: %s\n",tagphys,data->bodyname[tagphys]); + } + else + printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim],cp+1); } else printf("Physical groups of dimension %d not supported in %d-dimensional mesh: " "ignoring group %d %s",gmshtype,dim,tagphys,cp+1); } } - getline; + GETLINE; if(!strstr(line,"$EndPhysicalNames")) { printf("$PhysicalNames section should end to string $EndPhysicalNames:\n%s\n",line); } @@ -3356,13 +4065,13 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, int numPeriodicLinks; if(allocated) printf("Reading periodic links but doing nothing with them!\n"); - getline; + GETLINE; cp = line; numPeriodicLinks = next_int(&cp); for(i=1; i <= numPeriodicLinks; i++) { - getline; + GETLINE; } - getline; + GETLINE; if(!strstr(line,"$EndPeriodic")) { printf("$Periodic section should end to string $EndPeriodic:\n%s\n",line); } @@ -3371,42 +4080,42 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, else if(strstr(line,"$PartitionedEntities")) { if(allocated) printf("Reading partitioned entities but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndPartitionedEntities")) break; } } else if(strstr(line,"$NodeData")) { if(allocated) printf("Reading node data but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndNodeData")) break; } } else if(strstr(line,"$ElementData")) { if(allocated) printf("Reading element data but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndElementData")) break; } } else if(strstr(line,"$ElementNodeData")) { if(allocated) printf("Reading element node data but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndElementNodeData")) break; } } else if(strstr(line,"$GhostElements")) { if(allocated) printf("Reading ghost elements data but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndGhostElements")) break; } } else if(strstr(line,"$InterpolationScheme")) { if(allocated) printf("Reading interpolation scheme but doing nothing with them!\n"); for(;;) { - getline; + GETLINE; if(strstr(line,"$EndInterpolationScheme")) break; } } @@ -3454,7 +4163,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, if(k <= 0 || k > maxindx) printf("index out of bounds %d\n",k); else if(revindx[k] <= 0) - printf("unkonwn node %d %d in element %d\n",k,revindx[k],i); + printf("unknown node %d %d in element %d\n",k,revindx[k],i); else data->topology[i][j] = revindx[k]; } @@ -3464,785 +4173,2281 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, ElementsToBoundaryConditions(data,bound,FALSE,info); - /* The geometric entities are rather randomly numbered */ - if( usetaggeom ) { - RenumberBoundaryTypes(data,bound,TRUE,0,info); - RenumberMaterialTypes(data,bound,info); - } data->bodynamesexist = physvolexist; data->boundarynamesexist = physsurfexist; - + + if( tagsize > 0 ) free_Imatrix(tagmap,0,3,1,tagsize); + if(info) printf("Successfully read the mesh from the Gmsh input file.\n"); return(0); } - -int LoadGmshInput(struct FemType *data,struct BoundaryType *bound, - char *prefix,int info) +static int LoadGmshInput41(struct FemType *data,struct BoundaryType *bound, + char *filename,int usetaggeom, int info) { + int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; + int elemind[MAXNODESD2],elementtype; + int i,j,k,l,allocated,*revindx=NULL,maxindx; + int elemno, gmshtype, tagphys=0, tagpart, elemnodes,maxelemtype; + int tagmat,verno; + int physvolexist, physsurfexist,**tagmap,tagsize,maxtag[4]; FILE *in; - char line[MAXLINESIZE],filename[MAXFILESIZE]; - int errno; + const char manifoldname[4][10] = {"point", "line", "surface", "volume"}; + char *cp,line[MAXLINESIZE],longline[LONGLINESIZE]; - sprintf(filename,"%s",prefix); if ((in = fopen(filename,"r")) == NULL) { - sprintf(filename,"%s.msh",prefix); - if ((in = fopen(filename,"r")) == NULL) { - printf("LoadElmerInput: The opening of the mesh file %s failed!\n",filename); - return(1); - } + printf("The opening of the mesh file %s failed!\n",filename); + return(1); } + if(info) printf("Loading mesh in Gmsh format 4.1 from file %s\n",filename); - Getrow(line,in,FALSE); + allocated = FALSE; + dim = data->dim; + maxnodes = 0; + maxindx = 0; + maxelemtype = 0; + physvolexist = FALSE; + physsurfexist = FALSE; + usetaggeom = TRUE; /* The default */ + for(i=0;i<4;i++) maxtag[i] = 0; - if(info) { - printf("Format chosen using the first line: %s",line); - } +omstart: - if(strstr(line,"$")) { - int verno; - char *cp; - - Getrow(line,in,FALSE); - cp = line; - verno = next_int(&cp); - fclose(in); - - if( verno == 4 ) - errno = LoadGmshInput4(data,bound,filename,info); - else - errno = LoadGmshInput2(data,bound,filename,info); - - } else { - fclose(in); - printf("*****************************************************\n"); - printf("The first line did not start with $, assuming Gmsh 1 format\n"); - printf("This version of Gmsh format is no longer supported\n"); - printf("Please use Gsmh 2 or 4 versions for output\n"); - printf("*****************************************************\n"); - - errno = LoadGmshInput1(data,bound,filename,info); - } + for(;;) { + if(Getrow(line,in,FALSE)) goto end; + if(strstr(line,"$End")) continue; + + if(strstr(line,"$MeshFormat")) { + GETLINE; + cp = line; + verno = next_int(&cp); - return(errno); -} + if(verno != 4) { + printf("Version number is not compatible with the parser: %d\n",verno); + } + GETLINE; + if(!strstr(line,"$EndMeshFormat")) { + printf("$MeshFormat section should end to string $EndMeshFormat:\n%s\n",line); + } + } + + else if(strstr(line,"$Nodes")) { + int numEntityBlocks,tagEntity,dimEntity,parEntity,numNodes,ind; + int minNodeTag, maxNodeTag, parTag; + + GETLINE; + cp = line; + numEntityBlocks = next_int(&cp); + noknots = next_int(&cp); + minNodeTag = next_int(&cp); + maxNodeTag = next_int(&cp); + + if(allocated && info) printf("Reading %d nodes in %d blocks.\n",noknots,numEntityBlocks); + + k = 0; + + for(j=1; j <= numEntityBlocks; j++) { + GETLINE; + cp = line; + dimEntity = next_int(&cp); + tagEntity = next_int(&cp); + parTag = next_int(&cp); + numNodes = next_int(&cp); -static int UnvToElmerType(int unvtype) -{ + if( 0 && numNodes > 1 ) printf("Reading node block %d with %d nodes\n",j,numNodes); + + for(i=1; i <= numNodes; i++) { + GETLINE; + cp = line; + + ind = next_int(&cp); + + if( 0 && numNodes > 1 ) printf("block %d node %d ind %d %d\n",j,i,ind,k+i); + + if(allocated) { + if(maxindx > noknots) revindx[ind] = k+i; + } + else { + maxindx = MAX(ind,maxindx); + } + } + + for(i=1; i <= numNodes; i++) { + GETLINE; + cp = line; + + if(allocated) { + data->x[k+i] = next_real(&cp); + data->y[k+i] = next_real(&cp); + if(dim > 2) data->z[k+i] = next_real(&cp); + } + } + k += numNodes; + } + GETLINE; + + if(!strstr(line,"$EndNodes")) { + printf("$Nodes section should end to string $EndNodes:\n%s\n",line); + } + } + + else if(strstr(line,"$Entities")) { + int numPoints, numCurves, numSurfaces, numVolumes, numEnt; + int tag,tagdim,nophys,phystag; + int nobound, idum; + Real rdum; + + usetaggeom = FALSE; + + GETLINE; + cp = line; + numPoints = next_int(&cp); + numCurves = next_int(&cp); + numSurfaces = next_int(&cp); + numVolumes = next_int(&cp); + + if(allocated) { + tagsize = 0; + for(tagdim=0;tagdim<=3;tagdim++) + tagsize = MAX( tagsize, maxtag[tagdim]); + if(info) printf("Allocating lookup table for tags of size %d\n",tagsize); + if( tagsize > 0 ) { + tagmap = Imatrix(0,3,1,tagsize); + for(i=0;i<=3;i++) + for(j=1;j<=tagsize;j++) + tagmap[i][j] = 0; + } + } + + for(tagdim=0;tagdim<=3;tagdim++) { + + if( tagdim == 0 ) + numEnt = numPoints; + else if( tagdim == 1 ) + numEnt = numCurves; + else if( tagdim == 2 ) + numEnt = numSurfaces; + else if( tagdim == 3 ) + numEnt = numVolumes; + + if(!allocated) + maxtag[tagdim] = 0; + else if( maxtag[tagdim] > 0 ) + printf("Maximum original tag for %d %dDIM entities is %d\n",numEnt,tagdim,maxtag[tagdim]); + + if(numEnt > 0 && !allocated) printf("Reading %d entities in %dD\n",numEnt,tagdim); + + + for(i=1; i <= numEnt; i++) { + GETLONGLINE; + + // if( i==1 ) printf("1st line of dim %d with %d entries: %s\n",tagdim,numEnt,line); + + if( tagdim == 0 ) continue; + + cp = longline; + tag = next_int(&cp); + + if(!allocated) + maxtag[tagdim] = MAX( maxtag[tagdim], tag ); + + for(j=1;j<=6;j++) rdum = next_real(&cp); + nophys = next_int(&cp); + + if( nophys > 0 ) + phystag = next_int(&cp); + else + phystag = 0; + + if(allocated) tagmap[tagdim][tag] = phystag; + + + // The lines may be too long. So fill the string buffer until we get a newline. + j = k = 0; + for(;;) { + for(l=0;l 0 && !allocated) printf("Entity line %d has length %d.\n",i,k+j); + + //for(j=2;j<=nophys;j++) + // idum = next_int(&cp); + + //// if( tagdim == 0 ) continue; + + //nobound = next_int(&cp); + // for(j=1;j<=nobound;j++) + // idum = next_int(&cp); + } + } + + GETLONGLINE; + if(!strstr(longline,"$EndEntities")) { + printf("$Entities section should end to string $EndEntities:\n%s\n",longline); + } + } + + else if(strstr(line,"$Elements")) { + int numEntityBlocks, numElements, tagEntity, dimEntity, typeEle, NumElements; + int minElementTag, maxElementTag; + + GETLINE; + cp = line; + + k = 0; + numEntityBlocks = next_int(&cp); + noelements = next_int(&cp); + minElementTag = next_int(&cp); + maxElementTag = next_int(&cp); + + if(allocated) printf("Reading %d elements in %d blocks.\n",noelements,numEntityBlocks); + + + for(j=1; j<= numEntityBlocks; j++ ) { + + GETLINE; + cp = line; + + dimEntity = next_int(&cp); + tagEntity = next_int(&cp); + typeEle = next_int(&cp); + numElements = next_int(&cp); + + elementtype = GmshToElmerType(typeEle); + elemnodes = elementtype % 100; + maxelemtype = MAX(maxelemtype,elementtype); + + if( allocated && tagsize > 0 ) { + printf("Reading %d elements with tag %d of type %d\n", numElements, tagEntity, elementtype); + if( tagsize > 0 ) { + if( tagmap[dimEntity][tagEntity] ) { + printf("Mapping mesh tag %d to physical tag %d in %dDIM\n",tagEntity,tagmap[dimEntity][tagEntity],dimEntity); + tagEntity = tagmap[dimEntity][tagEntity]; + } + else { + printf("Mesh tag %d is not associated to any physical tag!\n",tagEntity); + } + } + } + + for(i=1; i <= numElements; i++) { + GETLINE; + cp = line; + + k += 1; + + elemno = next_int(&cp); + + if(allocated) { + data->elementtypes[k] = elementtype; + data->material[k] = tagEntity; + for(l=0;ltopology[k][l] = elemind[l]; + } + } + } + + GETLINE; + if(!strstr(line,"$EndElements")) { + printf("$Elements section should end to string $EndElements:\n%s\n",line); + } + } + + else if(strstr(line,"$PhysicalNames")) { + GETLINE; + cp = line; + nophysical = next_int(&cp); + for(i=0;iboundaryname[tagphys]); + printf("Boundary name for physical group %d is: %s\n",tagphys,data->boundaryname[tagphys]); + } + else + printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim-1],cp+1); + } + else if(gmshtype == dim) { + physvolexist = TRUE; + if(tagphys < MAXBODIES) { + sscanf(cp," \"%[^\"]\"",data->bodyname[tagphys]); + printf("Body name for physical group %d is: %s\n",tagphys,data->bodyname[tagphys]); + } + else + printf("Index %d too high: ignoring physical %s %s",tagphys,manifoldname[dim],cp+1); + } + else printf("Physical groups of dimension %d not supported in %d-dimensional mesh: " + "ignoring group %d %s",gmshtype,dim,tagphys,cp+1); + } + } + + GETLINE; + if(!strstr(line,"$EndPhysicalNames")) { + printf("$PhysicalNames section should end to string $EndPhysicalNames:\n%s\n",line); + } + } + else if(strstr(line,"$Periodic")) { + int numPeriodicLinks; + if(allocated) printf("Reading periodic links but doing nothing with them!\n"); + + GETLINE; + cp = line; + numPeriodicLinks = next_int(&cp); + for(i=1; i <= numPeriodicLinks; i++) { + GETLINE; + } + GETLINE; + if(!strstr(line,"$EndPeriodic")) { + printf("$Periodic section should end to string $EndPeriodic:\n%s\n",line); + } + } + + else if(strstr(line,"$PartitionedEntities")) { + if(allocated) printf("Reading partitioned entities but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndPartitionedEntities")) break; + } + } + else if(strstr(line,"$NodeData")) { + if(allocated) printf("Reading node data but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndNodeData")) break; + } + } + else if(strstr(line,"$ElementData")) { + if(allocated) printf("Reading element data but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndElementData")) break; + } + } + else if(strstr(line,"$ElementNodeData")) { + if(allocated) printf("Reading element node data but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndElementNodeData")) break; + } + } + else if(strstr(line,"$GhostElements")) { + if(allocated) printf("Reading ghost elements data but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndGhostElements")) break; + } + } + else if(strstr(line,"$InterpolationScheme")) { + if(allocated) printf("Reading interpolation scheme but doing nothing with them!\n"); + for(;;) { + GETLINE; + if(strstr(line,"$EndInterpolationScheme")) break; + } + } + else { + if(allocated) printf("Untreated command: %s",line); + } + + } + + end: + + + if(!allocated) { + if( noelements == 0 ) bigerror("No elements to load in Gmsh file!"); + if( noknots == 0 ) bigerror("No nodes to load in Gmsh file!"); + + maxnodes = maxelemtype % 100; + InitializeKnots(data); + data->dim = dim; + data->maxnodes = maxnodes; + data->noelements = noelements; + data->noknots = noknots; + + if(info) printf("Allocating for %d knots and %d elements.\n",noknots,noelements); + AllocateKnots(data); + + if(maxindx > noknots) { + revindx = Ivector(1,maxindx); + for(i=1;i<=maxindx;i++) revindx[i] = 0; + } + rewind(in); + allocated = TRUE; + goto omstart; + } + + if(maxindx > noknots) { + printf("Renumbering the Gmsh nodes from %d to %d\n",maxindx,noknots); + + for(i=1; i <= noelements; i++) { + elementtype = data->elementtypes[i]; + elemnodes = elementtype % 100; + + for(j=0;jtopology[i][j]; + if(k <= 0 || k > maxindx) + printf("index out of bounds %d\n",k); + else if(revindx[k] <= 0) + printf("unknown node %d %d in element %d\n",k,revindx[k],i); + else + data->topology[i][j] = revindx[k]; + } + } + free_Ivector(revindx,1,maxindx); + } + + ElementsToBoundaryConditions(data,bound,FALSE,info); + + data->bodynamesexist = physvolexist; + data->boundarynamesexist = physsurfexist; + + if( tagsize > 0 ) free_Imatrix(tagmap,0,3,1,tagsize); + + if(info) printf("Successfully read the mesh from the Gmsh input file.\n"); + + return(0); +} + +int LoadGmshInput(struct FemType *data,struct BoundaryType *bound, + char *prefix,int info) +{ + FILE *in; + char line[MAXLINESIZE],filename[MAXFILESIZE]; + int errnum,usetaggeom; + + sprintf(filename,"%s",prefix); + if ((in = fopen(filename,"r")) == NULL) { + sprintf(filename,"%s.msh",prefix); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadGmshInput: The opening of the mesh file %s failed!\n",filename); + return(1); + } + } + + Getrow(line,in,FALSE); + + if(info) { + printf("Format chosen using the first line: %s",line); + } + + if(strstr(line,"$")) { + int verno,minorno; + char *cp; + + Getrow(line,in,FALSE); + cp = line; + verno = next_int(&cp); + cp++; + minorno = next_int(&cp); + + if(info) printf("Gmsh version is %d.%d\n",verno,minorno); + + fclose(in); + + if( verno == 4 ) { + if( minorno == 0 ) + errnum = LoadGmshInput4(data,bound,filename,usetaggeom,info); + else if( minorno == 1 ) + errnum = LoadGmshInput41(data,bound,filename,usetaggeom,info); + else + printf("Minor version not yet supported, cannot continue!\n"); + } + else { + errnum = LoadGmshInput2(data,bound,filename,usetaggeom,info); + } + } else { + fclose(in); + printf("*****************************************************\n"); + printf("The first line did not start with $, assuming Gmsh 1 format\n"); + printf("This version of Gmsh format is no longer supported\n"); + printf("Please use Gsmh 2 or 4 versions for output\n"); + printf("*****************************************************\n"); + + errnum = LoadGmshInput1(data,bound,filename,info); + } + + if( info ) { + if( usetaggeom ) + printf("Using geometric numbering of entities\n"); + else + printf("Using physical numbering of entities\n"); + } + + return(errnum); +} + + + +int LoadFvcomMesh(struct FemType *data,struct BoundaryType *bound, + char *filename,int info) +{ + int noknots = 0,noelements = 0,maxnodes,dim; + int elemind[MAXNODESD2],elementtype; + int i,j,k,allocated,*revindx=NULL,maxindx; + int elemnodes,maxelemtype,elemtype0,bclines; + int tagmat,bccount; + int *bcinds,*bctags,nbc,nbc0,bc_id; + FILE *in; + char *cp,line[MAXLINESIZE]; + + + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadFVCOMInput: The opening of the FVCOM mesh file %s failed!\n",filename); + return(1); + } + if(info) printf("Loading mesh in FVCOM format from file %s\n",filename); + + allocated = FALSE; + dim = 2; + maxnodes = 0; + maxindx = 0; + maxelemtype = 303; + + noelements = 0; + bclines = 0; + + +omstart: + + noelements = 0; + noknots = 0; + nbc = 0; + nbc0 = 1; + bclines = 0; + bccount = 0; + + for(;;) { + if(Getrow(line,in,FALSE)) goto end; + if(line[0]=='\0') goto end; + + if(memcmp(line,"E3T",3) == 0 ) { + noelements += 1; + if(allocated) { + cp = line+4; + i = next_int(&cp); + if(i != noelements ) printf("Invalid element number: %d %d\n",noelements,i); + + data->elementtypes[i] = 303; + for(k=0;k<3;k++) + data->topology[i][k] = next_int(&cp); + data->material[i] = next_int(&cp); + } + } + else if(memcmp(line,"ND",2) == 0 ) { + noknots += 1; + if(allocated) { + cp = line+3; + i = next_int(&cp); + if(i != noknots ) printf("Invalid node number: %d %d\n",noknots,i); + data->x[i] = next_real(&cp); + data->y[i] = next_real(&cp); + if(dim > 2) data->z[i] = next_real(&cp); + } + } + else if(memcmp(line,"NS",2) == 0 ) { + bclines += 1; + + if(allocated){ + cp = line+3; + + for(i=0;i<10;i++) { + j = next_int(&cp); + + nbc += 1; + bcinds[nbc] = abs(j); + + if( j < 0 ) { + bccount += 1; + bc_id = next_int(&cp); + + for(k=nbc0;k<=nbc;k++) + bctags[k] = bc_id; + + nbc0 = nbc+1; + break; + } + } + } + } + else if(memcmp(line,"MESH2D",6) == 0 ) { + if(!allocated) printf("Yes, we have MESH2D as we should\n"); + } + else if(memcmp(line,"MESHNAME",8) == 0 ) { + if(!allocated) printf("Mesh name found but not used: %s\n",line+9); + } + } + + end: + + + if(!allocated) { + maxnodes = maxelemtype % 100; + InitializeKnots(data); + data->dim = dim; + data->maxnodes = maxnodes; + data->noelements = noelements; + data->noknots = noknots; + + if(info) printf("Allocating for %d knots and %d elements.\n",noknots,noelements); + AllocateKnots(data); + + printf("Number of BC lines: %d\n",bclines); + bcinds = Ivector(1,10*bclines); + bctags = Ivector(1,10*bclines); + for(i=1;i<=10*bclines;i++) bcinds[i] = bctags[i] = 0; + + rewind(in); + allocated = TRUE; + goto omstart; + } + + printf("Number of different BCs: %d\n",bccount); + printf("Number of BC nodes: %d\n",nbc); + + NodesToBoundaryChain(data,bound,bcinds,bctags,nbc,bccount,info); + + free_Ivector(bcinds,1,10*bclines); + free_Ivector(bctags,1,10*bclines); + + if(info) printf("Successfully read the mesh from the FVCOM file!\n"); + + return(0); +} + + +int LoadGeoInput(struct FemType *data,struct BoundaryType *bound, + char *filename,int info) +{ + int noknots = 0,noelements = 0,maxnodes,dim; + int elemind[MAXNODESD2],elementtype; + int i,j,k,allocated,*revindx=NULL,maxindx; + int elemnodes,maxelemtype,elemtype0; + int tagmat; + FILE *in; + char *cp,line[MAXLINESIZE]; + + + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadGeoInput: The opening of the mesh file %s failed!\n",filename); + return(1); + } + if(info) printf("Loading mesh in geo format from file %s\n",filename); + + allocated = FALSE; + dim = 3; + maxnodes = 0; + maxindx = 0; + maxelemtype = 0; + +omstart: + + + for(;;) { + if(Getrow(line,in,FALSE)) goto end; + if(line[0]=='\0') goto end; + if(strstr(line,"$End")) continue; + + if(strstr(line,"TYPES")) { + if(!strstr(line,"ALL=TET04")) { + printf("Only all tets implemnted at the monment!\n"); + return(1); + } + elemtype0 = 504; + GETLINE; + } + else if(strstr(line,"COORDINATES")) { + i = 0; + for(;;) { + GETLINE; + if( strstr(line,"END_COORDINATES")) break; + cp = line; + j = next_int(&cp); + i = i + 1; + if(allocated) { + if(maxindx > noknots) revindx[j] = i; + data->x[i] = next_real(&cp); + data->y[i] = next_real(&cp); + if(dim > 2) data->z[i] = next_real(&cp); + } + else { + maxindx = MAX(j,maxindx); + } + } + noknots = i; + } + else if(strstr(line,"ELEMENTS")) { + i = 0; + elementtype = elemtype0; + tagmat = 1; + + for(;;) { + GETLINE; + if( strstr(line,"END_ELEMENTS")) break; + cp = line; + j = next_int(&cp); + i = i + 1; + + if(allocated) { + elemnodes = elementtype % 100; + data->elementtypes[i] = elementtype; + data->material[i] = tagmat; + for(k=0;ktopology[i][k] = elemind[k]; + } + else { + maxelemtype = MAX(maxelemtype,elementtype); + } + } + noelements = i; + } + else if ( strstr(line,"BOUNDARIES")) { + for(;;) { + GETLINE; + if( strstr(line,"END_BOUNDARIES")) break; + + printf("Implement boundaries!\n"); + } + } + } + + end: + + + if(!allocated) { + maxnodes = maxelemtype % 100; + InitializeKnots(data); + data->dim = dim; + data->maxnodes = maxnodes; + data->noelements = noelements; + data->noknots = noknots; + + if(info) printf("Allocating for %d knots and %d elements.\n",noknots,noelements); + AllocateKnots(data); + + if(maxindx > noknots) { + revindx = Ivector(1,maxindx); + for(i=1;i<=maxindx;i++) revindx[i] = 0; + } + rewind(in); + allocated = TRUE; + goto omstart; + } + + if(maxindx > noknots) { + printf("Renumbering the Geo nodes from %d to %d\n",maxindx,noknots); + + for(i=1; i <= noelements; i++) { + elementtype = data->elementtypes[i]; + elemnodes = elementtype % 100; + + for(j=0;jtopology[i][j]; + if(k <= 0 || k > maxindx) + printf("index out of bounds %d\n",k); + else if(revindx[k] <= 0) + printf("unknown node %d %d in element %d\n",k,revindx[k],i); + else + data->topology[i][j] = revindx[k]; + } + } + free_Ivector(revindx,1,maxindx); + } + + if(0) ElementsToBoundaryConditions(data,bound,FALSE,info); + + if(info) printf("Successfully read the mesh from the Geo input file.\n"); + + return(0); +} + + +/* Mapping between the element type of Universal file format and + ElmerSolver element type. */ +static int UnvToElmerType(int unvtype) +{ int elmertype; - switch (unvtype) { + switch (unvtype) { + + case 11: + case 21: + elmertype = 202; + break; + + case 22: + case 23: + case 24: + elmertype = 203; + break; + + case 41: + case 51: + case 61: + case 74: + case 81: + case 91: + elmertype = 303; + break; + + case 42: + case 52: + case 62: + case 72: + case 82: + case 92: + elmertype = 306; + break; + + case 43: + case 53: + case 63: + case 73: + case 93: + elmertype = 310; + break; + + case 44: + case 54: + case 64: + case 71: + case 84: + case 94: + elmertype = 404; + break; + + case 45: + case 46: + case 56: + case 66: + case 76: + case 96: + elmertype = 408; + break; + + case 111: + elmertype = 504; + break; + + case 118: + elmertype = 510; + break; + + case 101: + case 112: + elmertype = 706; + break; + + case 102: + case 113: + elmertype = 715; + break; + + case 104: + case 115: + elmertype = 808; + break; + + case 105: + case 116: + elmertype = 820; + break; + + default: + elmertype = 0; + if(0) printf("Unknown elementtype in universal mesh format: %d\n",unvtype); + } + + return(elmertype); +} + + +/* The Universal format supports something as "degenerated" elements. + This means that the same node is given multiple times in the element + topology */ +static int UnvRedundantIndexes(int nonodes,int *ind) +{ + int i,j,redundant; + + redundant = FALSE; + for(i=0;ix[noknots] = next_real(&cp); + data->y[noknots] = next_real(&cp); + data->z[noknots] = next_real(&cp); + } + else { + if(nodeind != noknots) reordernodes = TRUE; + maxnodeind = MAX(maxnodeind,nodeind); + } + } + } + + if( mode == 2412 ) { + minelemtype = INT_MAX; + maxelemtype = 0; + + if(allocated && info) printf("Reading element topologies\n"); + for(;;) { + Getrow(line,in,FALSE); + if( strstr(line,"-1")) { + if(info && !allocated) printf("Element type range in mesh [%d,%d]\n",minelemtype,maxelemtype); + goto nextline; + } + + noelements += 1; + cp = line; + elid = next_int(&cp); + unvtype = next_int(&cp); + physind = next_int(&cp); + matind = next_int(&cp); + colorind = next_int(&cp); + nonodes = next_int(&cp); + + if(!allocated ) { + if(0) printf("elem = %d %d %d %d\n",noelements,unvtype,physind,matind); + } + + elmertype = UnvToElmerType(unvtype); + if(!elmertype) { + printf("Unknown elementtype %d %d %d %d %d %d %d\n", + noelements,elid,unvtype,physind,matind,colorind,nonodes); + printf("line %d: %s\n",linenumber,line); + bigerror("done"); + } + + if (!allocated) { + minphys = MIN( minphys, physind ); + maxphys = MAX( maxphys, physind ); + maxnodes = MAX(maxnodes, nonodes); + if(elid != noelements) reorderelements = TRUE; + maxelem = MAX(maxelem, elid); + } + + /* For beam elements there is a stupid additional row filled with zeros? */ + isbeam = ( elmertype / 100 == 2); + if(isbeam)Getrow(line,in,FALSE); + + Getrow(line,in,FALSE); + cp = line; + + if(elmertype == 510 ) + lines = 1; + else if(elmertype == 820 ) + lines = 2; + else + lines = 0; + + if(allocated) { + if(reorderelements) u2eelem[elid] = noelements; + + if(debug && !elementtypes[elmertype]) { + elementtypes[elmertype] = TRUE; + printf("new elementtype in elmer: %d (unv: %d)\n",elmertype,unvtype); + } + + if(elmertype % 100 != nonodes) { + printf("nonodes = %d elemtype = %d elid = %d\n",nonodes,elmertype,elid); + nonodes = elmertype % 100; + } + + data->elementtypes[noelements] = elmertype; + for(i=0;i 0 && i >= 8 ) { + if( i%8 == 0 ) { + Getrow(line,in,FALSE); + cp = line; + } + } + data->topology[noelements][i] = next_int(&cp); + } + + UnvRedundantIndexes(nonodes,data->topology[noelements]); + + UnvToElmerIndx(elmertype,data->topology[noelements]); + + /* should this be physical property or material property? */ + data->material[noelements] = physind + physoffset; + } + else { + minelemtype = MIN( minelemtype, elmertype ); + maxelemtype = MAX( maxelemtype, elmertype ); + for(i=1;i<=lines;i++) { + Getrow(line,in,FALSE); + } + } + } + } + + if( mode == 2420 ) { + int partuid,coordlabel,coordtype; + Real coeff; + if(allocated && info) printf("Reading Coordinate system information\n"); + + Getrow(line,in,FALSE); + if( !allocated ) { + cp = line; + partuid = next_int(&cp); + printf("Part UID = %d\n",partuid); + } + Getrow(line,in,FALSE); + if(!allocated ) { + sscanf(line,"%s",entityname); + printf("Part name = %s\n",entityname); + } + Getrow(line,in,FALSE); + if( !allocated ) { + cp = line; + coordlabel = next_int(&cp); + coordtype = next_int(&cp); + if( coordtype != 0 ) { + printf("Coordinate system is not cartesian: %d\n",coordtype); + printf("Code some more if you want to consider this!\n"); + } + } + + Getrow(line,in,FALSE); + if(!allocated ) { + sscanf(line,"%s",entityname); + printf("Coord system name = %s\n",entityname); + } + for(i=1;i<=4;i++) { + Getrow(line,in,FALSE); + if( !allocated ) { + cp = line; + if(!cp) printf("Problem reading line %d for coordinate system\n",i); + for(j=1;j<= 3;j++) { + coeff = next_real(&cp); + if( i == j ) { + scaling[i] = coeff; + if( fabs(coeff) < 1.0e-20) { + printf("Scaling for component %d too small %le\n",i,coeff); + } + else if( fabs(coeff-1.0) ) { + doscaling = TRUE; + printf("Scaling component %d by %le\n",i,coeff); + } + } + else { + if(fabs(coeff) > 1.0e-20 ) { + printf("Transformation matrix is not diagonal %d%d: %e\n",i,j,coeff); + smallerror("Code some more..."); + } + } + } + } + } + Getrow(line,in,FALSE); + if( strncmp(line," -1",6)) + printf("Field 2420 should already be ending: %s\n",line); + goto nextline; + } - case 11: - case 21: - elmertype = 202; - break; + if( mode == 780 ) { + int physind2,matind2; + maxelemtype = 0; + minelemtype = 1000; - case 22: - case 23: - elmertype = 203; - break; + if(allocated && info) printf("Reading element groups in mode %d\n",mode); + for(;;) { + Getrow(line,in,FALSE); + if( !strncmp(line," -1",6)) goto nextline; + + noelements += 1; + cp = line; + elid = next_int(&cp); + unvtype = next_int(&cp); - case 41: - case 51: - case 61: - case 74: - case 81: - case 91: - elmertype = 303; - break; + physind = next_int(&cp); + physind2 = next_int(&cp); + matind = next_int(&cp); + matind2 = next_int(&cp); + colorind = next_int(&cp); + nonodes = next_int(&cp); + + if (!allocated) { + maxnodes = MAX(maxnodes, nonodes); + if(elid != noelements) reorderelements = TRUE; + maxelem = MAX(maxelem, elid); + minphys = MIN( minphys, physind ); + maxphys = MAX( maxphys, physind ); + } + + if(unvtype == 11 || unvtype == 21) Getrow(line,in,FALSE); + Getrow(line,in,FALSE); + cp = line; + if(allocated) { + if(reorderelements) u2eelem[elid] = noelements; - case 42: - case 52: - case 62: - case 72: - case 82: - case 92: - elmertype = 306; - break; + elmertype = UnvToElmerType(unvtype); + maxelemtype = MAX( maxelemtype, elmertype ); + minelemtype = MIN( minelemtype, elmertype ); - case 43: - case 53: - case 63: - case 73: - case 93: - elmertype = 310; - break; + if(debug && !elementtypes[elmertype]) { + elementtypes[elmertype] = TRUE; + printf("new elementtype in elmer: %d (unv: %d)\n",elmertype,unvtype); + } - case 44: - case 54: - case 64: - case 71: - case 84: - case 94: - elmertype = 404; - break; + if(elmertype % 100 != nonodes) { + printf("nonodes = %d elemtype = %d elid = %d\n",nonodes,elmertype,elid); + nonodes = elmertype % 100; + } - case 45: - case 46: - case 56: - case 66: - case 76: - case 96: - elmertype = 408; - break; + data->elementtypes[noelements] = elmertype; + for(i=0;itopology[noelements][i] = next_int(&cp); - case 111: - elmertype = 504; - break; + UnvRedundantIndexes(nonodes,data->topology[noelements]); - case 118: - elmertype = 510; - break; + UnvToElmerIndx(elmertype,data->topology[noelements]); - case 112: - elmertype = 706; - break; + /* should this be physical property or material property? */ + data->material[noelements] = physind + physoffset; + } + } + } - case 113: - elmertype = 715; - break; + if( mode == 2467 || mode == 2435) { + if(allocated && info) printf("Reading element groups in mode %d\n",mode); + + for(;;) { + Getrow(line,in,FALSE); + if( !strncmp(line," -1",6)) goto nextline; + + cp = line; + nogroup = next_int(&cp); + maxelemtype = 0; + minelemtype = 1000; + for(i=1;i<=6;i++) + dummy = next_int(&cp); + noentities = next_int(&cp); - case 115: - elmertype = 808; - break; + if(!allocated) { + mingroup = MIN( mingroup, nogroup ); + maxgroup = MAX( maxgroup, nogroup ); + } - case 116: - elmertype = 820; - break; + Getrow(line,in,FALSE); + if( !strncmp(line," -1",6)) goto nextline; + + /* Used for the empty group created by salome */ + /* if( mode == 2467 && !strncmp(line," ",12)) continue; */ + + group++; + k = 0; + if(allocated) { + sscanf(line,"%s",entityname); + strcpy(data->bodyname[nogroup],entityname); + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; - default: - elmertype = 0; - printf("Unknown elementtype in universal mesh format: %d\n",unvtype); - } + if(info) printf("Reading %d:th group with index %d with %d entities: %s\n", + group,nogroup,noentities,entityname); + } + if(noentities == 0) Getrow(line,in,FALSE); - return(elmertype); -} + for(i=0;ielementtypes[ind]; + maxelemtype = MAX( maxelemtype, elemcode ); + minelemtype = MIN( minelemtype, elemcode ); + data->material[ind] = nogroup; + } + } + else if(grouptype == 7) { + nopoints += 1; + if(allocated) { + elemcode = 101; + data->material[noelements+nopoints] = nogroup; + maxelemtype = MAX( maxelemtype, elemcode ); + minelemtype = MIN( minelemtype, elemcode ); + data->elementtypes[noelements+nopoints] = elemcode; + data->topology[noelements+nopoints][0] = ind; + } + } + else { + printf("unknown group type %d\n",grouptype); + } + } + if(allocated && info) { + printf("Element type range in group is [%d %d]\n",minelemtype,maxelemtype); + } + + } + } + + if( mode == 164 ) { + if(!allocated) printf("Units dataset content is currently omitted!\n"); + for(;;) { + Getrow(line,in,FALSE); + if( !strncmp(line," -1",6)) + goto nextline; + } + } -static int UnvRedundantIndexes(int nonodes,int *ind) -{ - int i,j,redundant; - - redundant = FALSE; - for(i=0;inoknots = noknots; + data->noelements = noelements + nopoints; + data->maxnodes = maxnodes; + data->dim = dim; + + if(info) { + printf("Allocating mesh with %d nodes and %d %d-node elements in %d dims.\n", + noknots,noelements,maxnodes,dim); + } + AllocateKnots(data); + allocated = TRUE; + + /* Set an offset for physical indexes so that the defined groups and + existing physical indexes won't mix confusingly */ + if( maxphys >= mingroup && minphys <= maxgroup ) { + physoffset = maxgroup - minphys + 1; + } + else { + physoffset = 0; + } + if(info) { + printf("Physical index interval is [%d,%d]\n",minphys,maxphys); + if( maxgroup ) + printf("Group index interval is [%d,%d]\n",mingroup,maxgroup); + if(physoffset) printf("Using offset %d for physical indexes\n",physoffset); + } - reorder = FALSE; - switch (elemtype) { - - case 510: - reorder = TRUE; - porder = &order510[0]; - break; + goto omstart; + } + fclose(in); - case 408: - reorder = TRUE; - porder = &order408[0]; - break; + /* If the physical index may be zero, then we have a risk that there is + an unset material index. Elmer does not like material indexes of zeros. + This could be made prettier as now the almost same thing is done twice. */ + if( minphys + physoffset == 0 ) { + mingroup = INT_MAX; + maxgroup = 0; + for(i=1;i<=data->noelements;i++) { + mingroup = MIN( mingroup, data->material[i] ); + maxgroup = MAX( maxgroup, data->material[i] ); + } + if( mingroup == 0 ) { + if(info) { + if(!maxgroup) printf("No material groups were successfully applied\n"); + printf("Unset elements were given material index %d\n",maxgroup+1); + } + for(i=1;i<=data->noelements;i++) + if(data->material[i] == 0) data->material[i] = maxgroup + 1; + } + } - case 820: - reorder = TRUE; - porder = &order820[0]; - break; - + /* Elmer likes that node indexes are given so that no integers are missed. + If this is not the case we need to do renumbering of nodes. */ + if(reordernodes) { + printf("Reordering nodes continuously\n"); + for(j=1;j<=noelements;j++) + for(i=0;ielementtypes[j]%100;i++) + data->topology[j][i] = u2eind[data->topology[j][i]]; + free_Ivector(u2eind,1,maxnodeind); + } + if(reorderelements) { + free_Ivector(u2eelem,1,maxelem); } - if( reorder ) { - nodes = elemtype % 100; - for(i=0;ix; + else if( j == 2 ) + coord = data->y; + else + coord = data->z; + + if( fabs(scaling[j]-1.0) >= 1.0e-20 ) { + for(i=1;i<=noknots;i++) + coord[i] *= scaling[j]; + } + } } + + /* This is here for debugging of the nodal order */ + if(FALSE) for(j=1;j<=noelements;j++) { + int elemtype = data->elementtypes[j]; + printf("element = %d\n",j); + for(i=0;elemtype%100;i++) { + k = data->topology[j][i]; + printf("node i=%d %.3le %.3le %.3le\n",i,data->x[k],data->z[k],data->y[k]); + } + } + + + /* Until this far all elements have been listed as bulk elements. + Now separate the lower dimensional elements to be boundary elements. */ + ElementsToBoundaryConditions(data,bound,TRUE,info); + + if(info) printf("The Universal mesh was loaded from file %s.\n\n",filename); + + return(0); } -int LoadUniversalMesh(struct FemType *data,struct BoundaryType *bound, - char *prefix,int info) - /* Load the grid in universal file format */ + +int LoadCGsimMesh(struct FemType *data,char *prefix,int info) +/* Load the mesh from postprocessing format of CGsim */ { - int noknots,totknots,noelements,elemcode,maxnodes; - int allocated,maxknot,dim,ind,lines; - int reordernodes,reorderelements,nogroups,maxnodeind,maxelem,elid,unvtype,elmertype; - int nonodes,group,grouptype,mode,nopoints,nodeind,matind,physind,colorind; - int debug,mingroup,maxgroup,nogroup,noentities,dummy; - int *u2eind,*u2eelem; - int *elementtypes; + int noknots,noelements,maxnodes,material,allocated,dim,debug,thismat,thisknots,thiselems; char filename[MAXFILESIZE],line[MAXLINESIZE],*cp; - int i,j,k,l,n; - char entityname[MAXNAMESIZE]; + int i,j,inds[MAXNODESD2],savedofs; + Real dummyreal; FILE *in; strcpy(filename,prefix); if ((in = fopen(filename,"r")) == NULL) { - AddExtension(prefix,filename,"unv"); + AddExtension(prefix,filename,"plt"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadUniversalMesh: opening of the universal mesh file '%s' wasn't succesfull !\n", + printf("LoadCGsimMesh: opening of the CGsim mesh file '%s' wasn't successful !\n", filename); return(1); } } - - printf("Reading mesh from universal mesh file %s.\n",filename); - InitializeKnots(data); - dim = 3; - allocated = FALSE; - reordernodes = FALSE; - reorderelements = FALSE; + printf("Reading mesh from CGsim mesh file %s.\n",filename); + InitializeKnots(data); debug = FALSE; - if( debug ){ - elementtypes = Ivector(0,820); - for(i=0;i<=820;i++) elementtypes[i] = FALSE; - } - - maxnodeind = 0; - maxnodes = 0; - maxelem = 0; - + allocated = FALSE; + savedofs = FALSE; omstart: - if(info) { - if(allocated) - printf("Second round for reading data\n"); - else - printf("First round for allocating data\n"); - } - + maxnodes = 4; noknots = 0; noelements = 0; - nogroups = 0; - nopoints = 0; - group = 0; - - - for(;;) { + material = 0; + dim = 2; + thismat = 0; - if(0) printf("line: %d %s\n",mode,line); - nextline: - if( !strncmp(line," -1",6)) mode = 0; - if( Getrow(line,in,FALSE)) goto end; - if(!line) goto end; + for(;;) { + + if(Getrow(line,in,FALSE)) goto end; + if(line[0]=='\0') goto end; + + cp = strstr(line,"ZONE"); + if(!cp) continue; - if( !strncmp(line," -1",6)) mode = 0; - else if( !strncmp(line," 2411",6)) mode = 2411; - else if( !strncmp(line," 2412",6)) mode = 2412; - else if( !strncmp(line," 2467",6)) mode = 2467; - else if( !strncmp(line," 2435",6)) mode = 2435; - else if( !strncmp(line," 781",6)) mode = 781; - else if( !strncmp(line," 780",6)) mode = 780; - else if( allocated && strncmp(line," ",6)) printf("Unknown mode: %s",line); + thismat += 1; + cp = strstr(line," N="); + cp += 3; + thisknots = next_int(&cp); + cp = strstr(line,",E="); + cp += 3; + thiselems = next_int(&cp); - if(debug && mode) printf("Current mode is %d\n",mode); + if(debug) { + printf("%s",line); + printf("thismat = %d knots = %d elems = %d\n",thismat,thisknots,thiselems); + } - /* node definition */ - if( mode == 2411 || mode == 781 ) { - if(debug) printf("Reading nodes in mode %d\n",mode); - for(;;) { - GetrowDouble(line,in); - if( !strncmp(line," -1",6)) goto nextline; + for(i=1;i<=thisknots;i++) { + GETLINE; + if(allocated) { cp = line; - nodeind = next_int(&cp); - /* Three other fields omitted: two coordinate systems and color */ - noknots += 1; - GetrowDouble(line,in); - - if(allocated) { - if(reordernodes) { - if(u2eind[nodeind]) - printf("Reordering node %d already set (%d vs. %d)\n", - nodeind,u2eind[nodeind],noknots); - else - u2eind[nodeind] = noknots; - } + data->x[noknots+i] = next_real(&cp); + data->y[noknots+i] = next_real(&cp); + data->z[noknots+i] = 0.0; - cp = line; - data->x[noknots] = next_real(&cp); - data->y[noknots] = next_real(&cp); - data->z[noknots] = next_real(&cp); + if(savedofs == 1) { + for(j=1;j<=4;j++) + dummyreal = next_real(&cp); + data->dofs[1][noknots+i] = next_real(&cp); } - else { - if(nodeind != noknots) reordernodes = TRUE; - maxnodeind = MAX(maxnodeind,nodeind); + else if(savedofs == 5) { + for(j=1;j<=5;j++) + data->dofs[j][noknots+i] = next_real(&cp); } + } } - if( mode == 2412 ) { - if(debug) printf("Reading elements from field %d\n",mode); - for(;;) { - Getrow(line,in,FALSE); - if( !strncmp(line," -1",6)) goto nextline; - - noelements += 1; - cp = line; - elid = next_int(&cp); - unvtype = next_int(&cp); - physind = next_int(&cp); - matind = next_int(&cp); - colorind = next_int(&cp); - nonodes = next_int(&cp); - - if (!allocated) { - maxnodes = MAX(maxnodes, nonodes); - if(elid != noelements) reorderelements = TRUE; - maxelem = MAX(maxelem, elid); - } - - if(unvtype == 11 || unvtype == 21 || unvtype == 22 ) Getrow(line,in,FALSE); - Getrow(line,in,FALSE); + for(i=1;i<=thiselems;i++) { + GETLINE; + + if(allocated) { cp = line; + for(j=0;j<4;j++) + inds[j] = next_int(&cp); + for(j=0;j<4;j++) + data->topology[noelements+i][j] = inds[j]+noknots; + if(inds[2] == inds[3]) + data->elementtypes[noelements+i] = 303; + else + data->elementtypes[noelements+i] = 404; + data->material[noelements+i] = thismat; + } + } + + noknots += thisknots; + noelements += thiselems; + } + + end: + + if(!allocated) { + if(noknots == 0 || noelements == 0 || maxnodes == 0) { + printf("Invalid mesh consists of %d knots and %d %d-node elements.\n", + noknots,noelements,maxnodes); + fclose(in); + return(2); + } + + rewind(in); + data->noknots = noknots; + data->noelements = noelements; + data->maxnodes = maxnodes; + data->dim = dim; + + + if(info) { + printf("Allocating for %d knots and %d %d-node elements.\n", + noknots,noelements,maxnodes); + } + AllocateKnots(data); + + if(savedofs == 1) { + CreateVariable(data,1,1,0.0,"Temperature",FALSE); + } + else if(savedofs == 5) { + CreateVariable(data,1,1,0.0,"dTdX",FALSE); + CreateVariable(data,2,1,0.0,"dTdY",FALSE); + CreateVariable(data,3,1,0.0,"Qx",FALSE); + CreateVariable(data,4,1,0.0,"Qy",FALSE); + CreateVariable(data,5,1,0.0,"Temperature",FALSE); + } + + allocated = TRUE; + goto omstart; + } + fclose(in); + + if(info) printf("The CGsim mesh was loaded from file %s.\n\n",filename); + return(0); +} + + +int FluxToElmerType(int nonodes, int dim) { + int elmertype; - elmertype = UnvToElmerType(unvtype); - if(!elmertype) { - printf("Unknown elementtype %d %d %d %d %d %d\n", - elid,unvtype,physind,matind,colorind,nonodes); - printf("line: %s\n",line); - bigerror("done"); - } + elmertype = 0; + + if( dim == 2 ) { + switch( nonodes ) { + case 3: + elmertype = 203; + break; + case 6: + elmertype = 306; + break; + case 8: + elmertype = 408; + break; + } + } + + if( !elmertype ) printf("FluxToElmerType could not deduce element type! (%d %d)\n",nonodes,dim); - if(elmertype == 510 ) - lines = 1; - else if(elmertype == 820 ) - lines = 2; - else - lines = 0; + return(elmertype); +} - if(allocated) { - if(reorderelements) u2eelem[elid] = noelements; - if(debug && !elementtypes[elmertype]) { - elementtypes[elmertype] = TRUE; - printf("new elementtype in elmer: %d (unv: %d)\n",elmertype,unvtype); - } - if(elmertype % 100 != nonodes) { - printf("nonodes = %d elemtype = %d elid = %d\n",nonodes,elmertype,elid); - nonodes = elmertype % 100; - } - - - data->elementtypes[noelements] = elmertype; - for(i=0;i 0 && i >= 8 ) { - if( i%8 == 0 ) { - Getrow(line,in,FALSE); - cp = line; - } - } - data->topology[noelements][i] = next_int(&cp); - } - UnvRedundantIndexes(nonodes,data->topology[noelements]); - UnvToElmerIndx(elmertype,data->topology[noelements]); +int LoadFluxMesh(struct FemType *data,struct BoundaryType *bound, + char *prefix,int info) +/* Load the mesh from format of Flux Cedrat in TRA format. */ +{ + int noknots,noelements,maxnodes,dim,elmertype; + int nonodes,matind,noregions,mode; + int debug; + int *elementtypes; + char filename[MAXFILESIZE],line[MAXLINESIZE],*cp; + int i,j,k; + char entityname[MAXNAMESIZE]; + FILE *in; - /* should this be physical property or material property? */ - data->material[noelements] = physind; - } - else { - for(i=1;i<=lines;i++) - Getrow(line,in,FALSE); - } - } + + strcpy(filename,prefix); + if ((in = fopen(filename,"r")) == NULL) { + AddExtension(prefix,filename,"TRA"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadFluxMesh: opening of the Flux mesh file '%s' wasn't successful !\n", + filename); + return(1); } + } + + printf("Reading 2D mesh from Flux mesh file %s.\n",filename); + InitializeKnots(data); + debug = FALSE; + linenumber = 0; + dim = 2; + noknots = 0; + noelements = 0; + mode = 0; + maxnodes = 8; - if( mode == 780 ) { - int physind2,matind2; - if(debug) printf("Reading elements from field %d\n",mode); - for(;;) { - Getrow(line,in,FALSE); - if( !strncmp(line," -1",6)) goto nextline; - - noelements += 1; - cp = line; - elid = next_int(&cp); - unvtype = next_int(&cp); - physind = next_int(&cp); - physind2 = next_int(&cp); - matind = next_int(&cp); - matind2 = next_int(&cp); - colorind = next_int(&cp); - nonodes = next_int(&cp); - - if (!allocated) { - maxnodes = MAX(maxnodes, nonodes); - if(elid != noelements) reorderelements = TRUE; - maxelem = MAX(maxelem, elid); - } - - if(unvtype == 11 || unvtype == 21) Getrow(line,in,FALSE); - Getrow(line,in,FALSE); - cp = line; - if(allocated) { - if(reorderelements) u2eelem[elid] = noelements; + for(;;) { - elmertype = UnvToElmerType(unvtype); + if(0) printf("line: %d %s\n",mode,line); - if(debug && !elementtypes[elmertype]) { - elementtypes[elmertype] = TRUE; - printf("new elementtype in elmer: %d (unv: %d)\n",elmertype,unvtype); - } + if( Getrow(line,in,FALSE)) goto end; + if(line[0]=='\0') goto end; - if(elmertype % 100 != nonodes) { - printf("nonodes = %d elemtype = %d elid = %d\n",nonodes,elmertype,elid); - nonodes = elmertype % 100; - } + if( strstr(line,"Number of nodes")) mode = 1; + else if( strstr(line,"Total number of elements")) mode = 2; + else if( strstr(line,"Total number of regions")) mode = 3; - data->elementtypes[noelements] = elmertype; - for(i=0;itopology[noelements][i] = next_int(&cp); + else if( strstr(line,"Description of elements")) mode = 10; + else if( strstr(line,"Coordinates of the nodes")) mode = 11; + else if( strstr(line,"Names of the regions")) mode = 12; - UnvRedundantIndexes(nonodes,data->topology[noelements]); + else if( strstr(line,"Neighbouring element table")) mode = 13; + else if( strstr(line,"List of boundary nodes")) mode = 14; + else if( strstr(line,"Physical properties")) mode = 15; + else if( strstr(line,"Boundary conditions")) mode = 16; + else { + if(debug) printf("Unknown mode line %d: %s",linenumber,line); + mode = 0; + } - UnvToElmerIndx(elmertype,data->topology[noelements]); + if(debug && mode) printf("Current mode is %d\n",mode); - /* should this be physical property or material property? */ - data->material[noelements] = physind; - } - } - } + switch( mode ) { + case 1: + noknots = atoi(line); + break; - if( mode == 2467 || mode == 2435) { - if(debug) printf("Reading groups in mode %d\n",mode); - - for(;;) { + case 2: + noelements = atoi(line); + break; + + case 3: + noregions = atoi(line); + break; + + + case 10: + if(info) { + printf("Allocating mesh with %d nodes and %d %d-node elements in %d dims.\n", + noknots,noelements,maxnodes,dim); + } + + data->noknots = noknots; + data->noelements = noelements; + data->maxnodes = maxnodes; + data->dim = dim; + AllocateKnots(data); + + if(info) printf("Reading %d element topologies\n",noelements); + for(i=1;i<=noelements;i++) { Getrow(line,in,FALSE); - if( !strncmp(line," -1",6)) goto nextline; - cp = line; - nogroup = next_int(&cp); - for(i=1;i<=6;i++) - dummy = next_int(&cp); - noentities = next_int(&cp); - - Getrow(line,in,FALSE); - if( !strncmp(line," -1",6)) goto nextline; - - /* Used for the empty group created by salome */ - /* if( mode == 2467 && !strncmp(line," ",12)) continue; */ + j = next_int(&cp); + if( i != j ) { + printf("It seems that reordering of elements should be performed! (%d %d)\n",i,j); + } + nonodes = next_int(&cp); + matind = abs( next_int(&cp) ); - group++; - k = 0; - if(allocated) { - sscanf(line,"%s",entityname); - strcpy(data->bodyname[group],entityname); - data->bodynamesexist = TRUE; - data->boundarynamesexist = TRUE; + elmertype = FluxToElmerType( nonodes, dim ); + data->elementtypes[i] = elmertype; + data->material[i] = matind; - if(info) printf("Reading group %d with %d entities: %s\n", - nogroup,noentities,entityname); + Getrow(line,in,FALSE); + cp = line; + for(k=0;ktopology[i][k] = next_int(&cp); } - if(noentities == 0) Getrow(line,in,FALSE); - - for(i=0;ix[i] = next_real(&cp); + data->y[i] = next_real(&cp); + if(dim == 3) data->z[i] = next_real(&cp); + } + break; - if(ind == 0) continue; - if( grouptype == 8 ) { - if(allocated) { - if(reorderelements) ind = u2eelem[ind]; - elemcode = data->elementtypes[ind]; - data->material[ind] = group; - } - } - else if(grouptype == 7) { - nopoints += 1; - if(allocated) { - elemcode = 101; - data->material[noelements+nopoints] = group; - data->elementtypes[noelements+nopoints] = elemcode; - data->topology[noelements+nopoints][0] = ind; - } - } - else { - } + case 12: + if(info) printf("Reading %d names of regions\n",noregions); + for(i=1;i<=noregions;i++) { + Getrow(line,in,FALSE); + cp = line; + j = next_int(&cp); + if( i != j ) { + printf("It seems that reordering of regions should be performed! (%d %d)\n",i,j); } - if(k && allocated && info) - printf("Found new group %d with elements %d: %s\n",group,elemcode,entityname); - + sscanf(cp,"%s",entityname); + strcpy(data->bodyname[i],entityname); } - } + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + break; + + default: + if(debug) printf("unimplemented mode: %d\n",mode ); + mode = 0; + break; + } } -end: + end: + fclose(in); - exit; - if(info) printf("Done reading\n"); + /* Until this far all elements have been listed as bulk elements. + Now separate the lower dimensional elements to be boundary elements. */ + ElementsToBoundaryConditions(data,bound,TRUE,info); + + if(info) printf("The Flux mesh was loaded from file %s.\n\n",filename); + + return(0); +} - if(!allocated) { - if(reordernodes) { - if(info) printf("Reordering %d nodes with indexes up to %d\n",noknots,maxnodeind); - u2eind = Ivector(1,maxnodeind); - for(i=1;i<=maxnodeind;i++) u2eind[i] = 0; - } - if(reorderelements) { - if(info) printf("Reordering %d elements with indexes up to %d\n",noelements,maxelem); - u2eelem = Ivector(1,maxelem); - for(i=1;i<=maxelem;i++) u2eelem[i] = 0; - } +/* Mapping between the elemental node order of PF3 file format to + Elmer file format. */ +static void PF3ToElmerPermuteNodes(int elemtype,int *topology) +{ + int i=0, nodes=0, oldtopology[MAXNODESD2]; + int reorder, *porder; + int debug; + + int order303[] = {3,1,2}; //tri + int order306[] = {3,1,2,6,4,5}; //tri^2 + int order404[] = {3,4,1,2}; //quad + int order408[] = {3,4,1,2,7,8,5,6}; //quad^2 + int order504[] = {1,2,3,4}; //tetra + int order510[] = {1,2,3,4,5,8,6,7,10,9};//tetra^2 + int order605[] = {3,2,1,4,5}; //pyramid + int order613[] = {3,2,1,4,5,7,6,9,8,12,11,10,13}; //pyramid^2 + int order706[] = {6,4,5,3,1,2}; //wedge (prism) + int order715[] = {6,4,5,3,1,2,12,10,11,9,7,8,15,13,14}; //wedge^2 (prism^2) + int order808[] = {7,8,5,6,3,4,1,2}; //hexa + int order820[] = {7,8,5,6,3,4,1,2,15,16,13,14,19,20,17,18,11,12,9,10}; //hexa^2 + + debug = TRUE; + + reorder = FALSE; + + switch (elemtype) { + + case 101: + //nothing to change here + break; + + case 202: + //nothing to change here + break; + + case 203: + //nothing to change here + break; + + case 303: + reorder = TRUE; + porder = &order303[0]; + break; + + case 306: + reorder = TRUE; + porder = &order306[0]; + break; + + case 404: + reorder = TRUE; + porder = &order404[0]; + break; + + case 408: + reorder = TRUE; + porder = &order408[0]; + break; + + case 504: + reorder = TRUE; + porder = &order504[0]; + break; + + case 510: + reorder = TRUE; + porder = &order510[0]; + break; + + case 605: + reorder = TRUE; + porder = &order605[0]; + break; + + case 613: + reorder = TRUE; + porder = &order613[0]; + break; - if(noknots == 0 || noelements == 0 || maxnodes == 0) { - printf("Invalid mesh consits of %d knots and %d %d-node elements.\n", - noknots,noelements,maxnodes); - fclose(in); - return(2); - } + case 706: + reorder = TRUE; + porder = &order706[0]; + break; - rewind(in); - totknots = noknots; - data->noknots = noknots; - data->noelements = noelements + nopoints; - data->maxnodes = maxnodes; - data->dim = dim; + case 715: + reorder = TRUE; + porder = &order715[0]; + break; - if(info) { - printf("Allocating for %d knots and %d %d-node elements in %d dims.\n", - noknots,noelements,maxnodes,dim); - } - AllocateKnots(data); - allocated = TRUE; - - goto omstart; - } - fclose(in); + case 808: + reorder = TRUE; + porder = &order808[0]; + break; + case 820: + reorder = TRUE; + porder = &order820[0]; + break; - if(reordernodes) { - for(j=1;j<=noelements;j++) - for(i=0;ielementtypes[j]%100;i++) - data->topology[j][i] = u2eind[data->topology[j][i]]; - free_Ivector(u2eind,1,maxnodeind); + default: + if(debug) printf("Warning : Unknown element type: %d\n",elemtype ); + break; } - if(reorderelements) { - free_Ivector(u2eelem,1,maxelem); + + if( reorder ) { + nodes = elemtype % 100; + for(i=0;imaterial[1]; - for(i=1;i<=data->noelements;i++) { - mingroup = MIN( mingroup, data->material[i]); - maxgroup = MAX( maxgroup, data->material[i]); +int FluxToElmerType3D(int nonodes, int dim) { + int elmertype; + + elmertype = 0; + + if( dim == 2 ) { + switch( nonodes ) { + case 3: + elmertype = 303; + break; + case 4: + elmertype = 404; + break; + case 6: + elmertype = 306; + break; + case 8: + elmertype = 408; + break; + } } - if(info) printf("The group interval is [%d,%d]\n",mingroup,maxgroup); - if(mingroup == 0) { - if(info) { - if(!maxgroup) printf("No material groups were successfully applied\n"); - printf("Unset elements were given material index %d\n",maxgroup+1); + + if( dim == 3 ) { + switch( nonodes ) { + case 4: + elmertype = 504; + break; + case 5: + elmertype = 605; + break; + case 6: + elmertype = 706; + break; + case 8: + elmertype = 808; + break; + case 10: + elmertype = 510; + break; + case 13: + elmertype = 613; + break; + case 15: + elmertype = 715; + break; + case 20: + elmertype = 820; + break; } - for(i=1;i<=data->noelements;i++) - if(data->material[i] == 0) data->material[i] = maxgroup + 1; } - ElementsToBoundaryConditions(data,bound,TRUE,info); - - if(info) printf("The Universal mesh was loaded from file %s.\n\n",filename); + if( !elmertype ) printf("FluxToElmerType3D could not deduce element type! (%d %d)\n",nonodes,dim); - return(0); + return(elmertype); } - - -int LoadCGsimMesh(struct FemType *data,char *prefix,int info) -/* Load the mesh from postprocessing format of CGsim */ +int LoadFluxMesh3D(struct FemType *data,struct BoundaryType *bound, + char *prefix,int info) +/* Load the mesh from format of Flux Cedrat in PF3 format. */ { - int noknots,noelements,maxnodes,material,allocated,dim,debug,thismat,thisknots,thiselems; + int noknots,noelements,maxnodes,dim,elmertype; + int nonodes,matind,noregions,mode; + int dimplusone, maxlinenodes, nodecnt; + int debug; + int *elementtypes; char filename[MAXFILESIZE],line[MAXLINESIZE],*cp; - int i,j,inds[MAXNODESD2],savedofs; - Real dummyreal; + int i,j,k; + char entityname[MAXNAMESIZE]; FILE *in; - strcpy(filename,prefix); if ((in = fopen(filename,"r")) == NULL) { - AddExtension(prefix,filename,"plt"); + AddExtension(prefix,filename,"PF3"); if ((in = fopen(filename,"r")) == NULL) { - printf("LoadCGsimMesh: opening of the CGsim mesh file '%s' wasn't succesfull !\n", + printf("LoadFluxMesh3D: opening of the Flux mesh file '%s' wasn't successful !\n", filename); return(1); } } - - printf("Reading mesh from CGsim mesh file %s.\n",filename); + + printf("Reading 3D mesh from Flux mesh file %s.\n",filename); InitializeKnots(data); debug = FALSE; - allocated = FALSE; - savedofs = FALSE; - -omstart: - - maxnodes = 4; + linenumber = 0; + dim = 3; noknots = 0; noelements = 0; - material = 0; - dim = 2; - thismat = 0; - + mode = 0; + maxnodes = 20; // 15? + maxlinenodes = 12; //nodes can be located at several lines - for(;;) { - + for(;;) { - if(Getrow(line,in,FALSE)) goto end; - if(!line) goto end; - - cp = strstr(line,"ZONE"); - if(!cp) continue; - + if(0) printf("line: %d %s\n",mode,line); - thismat += 1; - cp = strstr(line," N="); - cp += 3; - thisknots = next_int(&cp); + if( Getrow(line,in,FALSE)) goto end; + if(line[0]=='\0') goto end; + if( strstr(line,"==== DECOUPAGE TERMINE")) goto end; - cp = strstr(line,",E="); - cp += 3; - thiselems = next_int(&cp); + if( strstr(line,"NOMBRE DE DIMENSIONS DU DECOUPAGE")) mode = 1; + else if( strstr(line,"NOMBRE D'ELEMENTS")) mode = 3; + else if( strstr(line,"NOMBRE DE POINTS")) mode = 2; + else if( strstr(line,"NOMBRE DE REGIONS")) mode = 4; - if(debug) { - printf("%s",line); - printf("thismat = %d knots = %d elems = %d\n",thismat,thisknots,thiselems); + else if( strstr(line,"DESCRIPTEUR DE TOPOLOGIE DES ELEMENTS")) mode = 10; + else if( strstr(line,"COORDONNEES DES NOEUDS")) mode = 11; + else if( strstr(line,"NOMS DES REGIONS")) mode = 12; + else { + if(debug) printf("Unknown mode line %d: %s",linenumber,line); + mode = 0; } - for(i=1;i<=thisknots;i++) { - getline; + if(debug && mode) printf("Current mode is %d\n",mode); - if(allocated) { - cp = line; - data->x[noknots+i] = next_real(&cp); - data->y[noknots+i] = next_real(&cp); - data->z[noknots+i] = 0.0; + switch( mode ) { + case 1: + dim = atoi(line); + break; - if(savedofs == 1) { - for(j=1;j<=4;j++) - dummyreal = next_real(&cp); - data->dofs[1][noknots+i] = next_real(&cp); - } - else if(savedofs == 5) { - for(j=1;j<=5;j++) - data->dofs[j][noknots+i] = next_real(&cp); + case 2: + if( strstr(line,"NOMBRE DE POINTS D'INTEGRATION")) break;/* We are looking for the total number of nodes */ + noknots = atoi(line); + break; + + case 3: + i = atoi(line); + noelements = MAX(i,noelements); /* We are looking for the total number of elements */ + break; + + case 4: + i = atoi(line); + noregions = MAX(i,noregions); /* We are looking for the total number of regions */ + break; + + + case 10: + if(info) { + printf("Allocating mesh with %d nodes and %d %d-node elements in %d dims.\n", + noknots,noelements,maxnodes,dim); + } + + data->noknots = noknots; + data->noelements = noelements; + data->maxnodes = maxnodes; + data->dim = dim; + AllocateKnots(data); + + if(info) printf("Reading %d element topologies\n",noelements); + for(i=1;i<=noelements;i++) + { + Getrow(line,in,FALSE); + cp = line; + j = next_int(&cp); + if( i != j ) { + printf("It seems that reordering of elements should be performed! (%d %d)\n",i,j); } + next_int(&cp); //2 internal element type description + next_int(&cp); //3 internal element type description + matind = next_int(&cp); //4 number of the belonging region + dimplusone = next_int(&cp); //5 dimensiality 4-3D 3-2D + next_int(&cp); //6 zero here always + next_int(&cp); //7 internal element type description + nonodes = next_int(&cp); //8 number of nodes + + elmertype = FluxToElmerType3D( nonodes, dimplusone-1 ); + data->elementtypes[i] = elmertype; + data->material[i] = matind; - } - } + Getrow(line,in,FALSE); + cp = line; + nodecnt = 0; + for(k=0;k= maxlinenodes) { + nodecnt = 0; + Getrow(line,in,FALSE); + cp = line; + } + data->topology[i][k] = next_int(&cp); + nodecnt+=1; + } + + PF3ToElmerPermuteNodes(elmertype,data->topology[noelements]); + + } + break; - if(allocated) { + case 11: + if(info) printf("Reading %d element nodes\n",noknots); + for(i=1;i<=noknots;i++) { + Getrow(line,in,FALSE); cp = line; - for(j=0;j<4;j++) - inds[j] = next_int(&cp); - for(j=0;j<4;j++) - data->topology[noelements+i][j] = inds[j]+noknots; - if(inds[2] == inds[3]) - data->elementtypes[noelements+i] = 303; - else - data->elementtypes[noelements+i] = 404; - data->material[noelements+i] = thismat; + j = next_int(&cp); + if( i != j ) { + printf("It seems that reordering of nodes should be performed! (%d %d)\n",i,j); + } + data->x[i] = next_real(&cp); + data->y[i] = next_real(&cp); + data->z[i] = next_real(&cp); } - } + break; - noknots += thisknots; - noelements += thiselems; - } - end: + case 12: + if(info) printf("Reading %d names of regions\n",noregions); + for(i=1;i<=noregions;i++) { + Getrow(line,in,FALSE); - if(!allocated) { - if(noknots == 0 || noelements == 0 || maxnodes == 0) { - printf("Invalid mesh consits of %d knots and %d %d-node elements.\n", - noknots,noelements,maxnodes); - fclose(in); - return(2); - } + /* currently we just cycle through this and get a new row */ + if( strstr(line,"REGIONS SURFACIQUES")) Getrow(line,in,FALSE); + if( strstr(line,"REGIONS VOLUMIQUES")) Getrow(line,in,FALSE); - rewind(in); - data->noknots = noknots; - data->noelements = noelements; - data->maxnodes = maxnodes; - data->dim = dim; + sscanf(line,"%s",entityname); + strcpy(data->bodyname[i],entityname); + } + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + break; - - if(info) { - printf("Allocating for %d knots and %d %d-node elements.\n", - noknots,noelements,maxnodes); - } - AllocateKnots(data); - if(savedofs == 1) { - CreateVariable(data,1,1,0.0,"Temperature",FALSE); - } - else if(savedofs == 5) { - CreateVariable(data,1,1,0.0,"dTdX",FALSE); - CreateVariable(data,2,1,0.0,"dTdY",FALSE); - CreateVariable(data,3,1,0.0,"Qx",FALSE); - CreateVariable(data,4,1,0.0,"Qy",FALSE); - CreateVariable(data,5,1,0.0,"Temperature",FALSE); + default: + if(debug) printf("unimplemented mode: %d\n",mode ); + mode = 0; + break; } - - allocated = TRUE; - goto omstart; } + + end: fclose(in); - if(info) printf("The CGsim mesh was loaded from file %s.\n\n",filename); + /* Until this far all elements have been listed as bulk elements. + Now separate the lower dimensional elements to be boundary elements. */ + ElementsToBoundaryConditions(data,bound,TRUE,info); + + if(info) printf("The Flux 3D mesh was loaded from file %s.\n\n",filename); + return(0); } - - diff --git a/ElmerGUI/Application/plugins/egconvert.h b/ElmerGUI/Application/plugins/egconvert.h index 0ab6e41697..92cc3bbcd0 100644 --- a/ElmerGUI/Application/plugins/egconvert.h +++ b/ElmerGUI/Application/plugins/egconvert.h @@ -1,15 +1,20 @@ -/* femfilein.h */ -/* Routines for importing existing FEM meshes */ - -int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadFidapInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadNastranInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadMeditInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadComsolMesh(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadGidInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadGmshInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadUniversalMesh(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); -int LoadCGsimMesh(struct FemType *data,char *prefix,int info); +/* femfilein.h -> egconvert.h */ +/* Routines for importing meshes and data from other formats. */ + +int LoadAbaqusInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadAbaqusOutput(struct FemType *data,char *prefix,int info); +int LoadFidapInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadNastranInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadFieldviewInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadTriangleInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadMeditInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadComsolMesh(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadGidInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadGmshInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadGeoInput(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadFvcomMesh(struct FemType *data,struct BoundaryType *bound,char *filename,int info); +int LoadUniversalMesh(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadCGsimMesh(struct FemType *data,char *prefix,int info); +int LoadFluxMesh(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); +int LoadFluxMesh3D(struct FemType *data,struct BoundaryType *bound,char *prefix,int info); diff --git a/ElmerGUI/Application/plugins/egmain.cpp b/ElmerGUI/Application/plugins/egmain.cpp index 0cff444939..8b21f6f17c 100644 --- a/ElmerGUI/Application/plugins/egmain.cpp +++ b/ElmerGUI/Application/plugins/egmain.cpp @@ -82,653 +82,7 @@ static int Inmethod; int info=TRUE,nogrids=0,nomeshes=0,activemesh=0; -const char *IOmethods[] = { - /*0*/ "EG", - /*1*/ "ELMERGRID", - /*2*/ "ELMERSOLVER", - /*3*/ "ELMERPOST", - /*4*/ "ANSYS", - /*5*/ "IDEAS", - /*6*/ "NASTRAN", - /*7*/ "FIDAP", - /*8*/ "UNV", - /*9*/ "COMSOL", - /*10*/ "FIELDVIEW", - /*11*/ "TRIANGLE", - /*12*/ "MEDIT", - /*13*/ "GID", - /*14*/ "GMSH", - /*15*/ "PARTITIONED", - /*16*/ "CGSIM", -}; - - - -int InlineParameters(struct ElmergridType *eg,int argc,char *argv[],const char *IOmethods[],int first,int info) -{ - int arg,i,dim; - char command[MAXLINESIZE]; - - dim = eg->dim; - - /* Type of input file */ - if(first > 3) { - for(i=0;iinmethod = i; - break; - } - } - if(i>MAXFORMATS) eg->inmethod = atoi(argv[1]); - - - /* Type of output file (fewer options) */ - strcpy(command,argv[2]); - for(i=0;ioutmethod = i; - break; - } - } - if(i>MAXFORMATS) eg->outmethod = atoi(argv[2]); - - /* Name of output file */ - strcpy(eg->filesin[0],argv[3]); - strcpy(eg->filesout[0],eg->filesin[0]); - strcpy(eg->mapfile,eg->filesin[0]); - } - - - /* The optional inline parameters */ - - for(arg=first;arg silent = TRUE; - info = FALSE; - } - - if(strcmp(argv[arg],"-in") ==0 ) { - if(arg+1 >= argc) { - printf("The secondary input file name is required as a parameter\n"); - return(1); - } - else { - strcpy(eg->filesin[eg->nofilesin],argv[arg+1]); - printf("A secondary input file %s will be loaded.\n",eg->filesin[eg->nofilesin]); - eg->nofilesin++; - } - } - - if(strcmp(argv[arg],"-out") == 0) { - if(arg+1 >= argc) { - printf("The output name is required as a parameter\n"); - return(2); - } - else { - strcpy(eg->filesout[0],argv[arg+1]); - } - } - - if(strcmp(argv[arg],"-decimals") == 0) { - eg->decimals = atoi(argv[arg+1]); - } - - if(strcmp(argv[arg],"-triangles") ==0) { - eg->triangles = TRUE; - printf("The rectangles will be split to triangles.\n"); - if(arg+1 < argc) { - if(strcmp(argv[arg+1],"-")) { - eg->triangleangle = atof(argv[arg+1]); - } - } - } - - if(strcmp(argv[arg],"-merge") == 0) { - if(arg+1 >= argc) { - printf("Give a parameter for critical distance.\n"); - return(3); - } - else { - eg->merge = TRUE; - eg->cmerge = atof(argv[arg+1]); - } - } - - if(strcmp(argv[arg],"-relh") == 0) { - if(arg+1 >= argc) { - printf("Give a relative mesh density related to the specifications\n"); - return(3); - } - else { - eg->relh = atof(argv[arg+1]); - } - } - - if(strcmp(argv[arg],"-order") == 0) { - if(arg+dim >= argc) { - printf("Give %d parameters for the order vector.\n",dim); - return(4); - } - else { - eg->order = TRUE; - eg->corder[0] = atof(argv[arg+1]); - eg->corder[1] = atof(argv[arg+2]); - if(dim==3) eg->corder[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-autoorder") == 0) { - eg->order = 2; - } - - if(strcmp(argv[arg],"-halo") == 0) { - eg->partitionhalo = TRUE; - } - if(strcmp(argv[arg],"-indirect") == 0) { - eg->partitionindirect = TRUE; - } - if(strcmp(argv[arg],"-metisorder") == 0) { - eg->order = 3; - } - if(strcmp(argv[arg],"-centralize") == 0) { - eg->center = TRUE; - } - if(strcmp(argv[arg],"-scale") == 0) { - if(arg+dim >= argc) { - printf("Give %d parameters for the scaling.\n",dim); - return(5); - } - else { - eg->scale = TRUE; - eg->cscale[0] = atof(argv[arg+1]); - eg->cscale[1] = atof(argv[arg+2]); - if(dim==3) eg->cscale[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-translate") == 0) { - if(arg+dim >= argc) { - printf("Give %d parameters for the translate vector.\n",dim); - return(6); - } - else { - eg->translate = TRUE; - eg->ctranslate[0] = atof(argv[arg+1]); - eg->ctranslate[1] = atof(argv[arg+2]); - if(dim == 3) eg->ctranslate[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-saveinterval") == 0) { - if(arg+dim >= argc) { - printf("Give min, max and step for the interval.\n"); - return(7); - } - else { - eg->saveinterval[0] = atoi(argv[arg+1]); - eg->saveinterval[1] = atoi(argv[arg+2]); - eg->saveinterval[2] = atoi(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-rotate") == 0 || strcmp(argv[arg],"-rotate") == 0) { - if(arg+dim >= argc) { - printf("Give three parameters for the rotation angles.\n"); - return(8); - } - else { - eg->rotate = TRUE; - eg->crotate[0] = atof(argv[arg+1]); - eg->crotate[1] = atof(argv[arg+2]); - eg->crotate[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-clone") == 0) { - if(arg+dim >= argc) { - printf("Give the number of clones in each %d directions.\n",dim); - return(9); - } - else { - eg->clone[0] = atoi(argv[arg+1]); - eg->clone[1] = atoi(argv[arg+2]); - if(dim == 3) eg->clone[2] = atoi(argv[arg+3]); - } - } - if(strcmp(argv[arg],"-clonesize") == 0) { - if(arg+dim >= argc) { - printf("Give the clone size in each %d directions.\n",dim); - return(10); - } - else { - eg->clonesize[0] = atof(argv[arg+1]); - eg->clonesize[1] = atof(argv[arg+2]); - if(dim == 3) eg->clonesize[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-unite") == 0) { - eg->unitemeshes = TRUE; - printf("The meshes will be united.\n"); - } - - if(strcmp(argv[arg],"-names") == 0) { - eg->usenames = TRUE; - printf("Names will be conserved when possible\n"); - } - - if(strcmp(argv[arg],"-removelowdim") == 0) { - eg->removelowdim = TRUE; - printf("Lower dimensional boundaries will be removed\n"); - } - - if(strcmp(argv[arg],"-removeunused") == 0) { - eg->removeunused = TRUE; - printf("Nodes that do not appear in any element will be removed\n"); - } - - if(strcmp(argv[arg],"-autoclean") == 0) { - eg->removelowdim = TRUE; - eg->bulkorder = TRUE; - eg->boundorder = TRUE; - eg->removeunused = TRUE; - printf("Lower dimensional boundaries will be removed\n"); - printf("Materials and boundaries will be renumbered\n"); - printf("Nodes that do not appear in any element will be removed\n"); - } - - if(strcmp(argv[arg],"-polar") == 0) { - eg->polar = TRUE; - printf("Making transformation to polar coordinates.\n"); - if(arg+1 >= argc) { - printf("The preferred radius is required as a parameter\n"); - eg->polarradius = 1.0; - } - else { - eg->polarradius = atoi(argv[arg+1]); - } - } - - if(strcmp(argv[arg],"-cylinder") == 0) { - eg->cylinder = TRUE; - printf("Making transformation from cylindrical to cartesian coordinates.\n"); - } - - if(strcmp(argv[arg],"-reduce") == 0) { - if(arg+2 >= argc) { - printf("Give two material for the interval.\n"); - return(12); - } - else { - eg->reduce = TRUE; - eg->reducemat1 = atoi(argv[arg+1]); - eg->reducemat2 = atoi(argv[arg+2]); - } - } - if(strcmp(argv[arg],"-increase") == 0) { - eg->increase = TRUE; - } - if(strcmp(argv[arg],"-bulkorder") == 0) { - eg->bulkorder = TRUE; - } - if(strcmp(argv[arg],"-boundorder") == 0) { - eg->boundorder = TRUE; - } - if(strcmp(argv[arg],"-pelem") == 0) { - for(i=arg+1;ipelemmap[3*eg->pelems+i-1-arg] = atoi(argv[i]); - eg->pelems++; - } - if(strcmp(argv[arg],"-belem") == 0) { - for(i=arg+1;ibelemmap[3*eg->belems+i-1-arg] = atoi(argv[i]); - eg->belems++; - } - if(strcmp(argv[arg],"-partition") == 0) { - if(arg+dim >= argc) { - printf("The number of partitions in %d dims is required as parameters.\n",dim); - return(13); - } - else { - eg->partitions = 1; - eg->partdim[0] = atoi(argv[arg+1]); - eg->partdim[1] = atoi(argv[arg+2]); - if(dim == 3) eg->partdim[2] = atoi(argv[arg+3]); - eg->partitions = 1; - for(i=0;i<3;i++) { - if(eg->partdim[i] == 0) eg->partdim[i] = 1; - eg->partitions *= eg->partdim[i]; - } - eg->partopt = 0; - if(arg+4 < argc) - if(argv[arg+4][0] != '-') eg->partopt = atoi(argv[arg+4]); - - printf("The mesh will be partitioned with simple division to %d partitions.\n", - eg->partitions); - } - } - if(strcmp(argv[arg],"-partorder") == 0) { - if(arg+dim >= argc) { - printf("Give %d parameters for the order vector.\n",dim); - return(14); - } - else { - eg->partorder = 1; - eg->partcorder[0] = atof(argv[arg+1]); - eg->partcorder[1] = atof(argv[arg+2]); - if(dim==3) eg->partcorder[2] = atof(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-metis") == 0) { -#if HAVE_METIS - if(arg+1 >= argc) { - printf("The number of partitions is required as a parameter\n"); - return(15); - } - else { - eg->metis = atoi(argv[arg+1]); - printf("The mesh will be partitioned with Metis to %d partitions.\n",eg->metis); - eg->partopt = 0; - if(arg+2 < argc) - if(argv[arg+2][0] != '-') eg->partopt = atoi(argv[arg+2]); - } -#else - printf("This version of ElmerGrid was compiled without Metis library!\n"); -#endif - } - - if(strcmp(argv[arg],"-periodic") == 0) { - if(arg+dim >= argc) { - printf("Give the periodic coordinate directions (e.g. 1 1 0)\n"); - return(16); - } - else { - eg->periodicdim[0] = atoi(argv[arg+1]); - eg->periodicdim[1] = atoi(argv[arg+2]); - if(dim == 3) eg->periodicdim[2] = atoi(argv[arg+3]); - } - } - - if(strcmp(argv[arg],"-discont") == 0) { - if(arg+1 >= argc) { - printf("Give the discontinuous boundary conditions.\n"); - return(17); - } - else { - eg->discontbounds[eg->discont] = atoi(argv[arg+1]); - eg->discont++; - } - } - - if(strcmp(argv[arg],"-connect") == 0) { - if(arg+1 >= argc) { - printf("Give the connected boundary conditions.\n"); - return(10); - } - else { - eg->connectbounds[eg->connect] = atoi(argv[arg+1]); - eg->connect++; - } - } - - if(strcmp(argv[arg],"-boundbound") == 0) { - for(i=arg+1;i<=arg+3 && iboundbound[3*eg->boundbounds+i-(1+arg)] = atoi(argv[i]); - if((i-arg)%3 == 0) eg->boundbounds++; - } - } - if(strcmp(argv[arg],"-bulkbound") == 0) { - for(i=arg+1;i<=arg+3 && ibulkbound[3*eg->bulkbounds+i-(1+arg)] = atoi(argv[i]); - if((i-arg)%3 == 0) eg->bulkbounds++; - } - } - if(strcmp(argv[arg],"-boundtype") == 0) { - for(i=arg+1;isidemap[3*eg->sidemappings+i-1-arg] = atoi(argv[i]); - eg->sidemappings++; - } - if(strcmp(argv[arg],"-bulktype") == 0) { - for(i=arg+1;ibulkmap[3*eg->bulkmappings+i-1-arg] = atoi(argv[i]); - eg->bulkmappings++; - } - - if(strcmp(argv[arg],"-layer") == 0) { - if(arg+4 >= argc) { - printf("Give four parameters for the layer: boundary, elements, thickness, ratio.\n"); - return(18); - } - else if(eg->layers == MAXBOUNDARIES) { - printf("There can only be %d layers, sorry.\n",MAXBOUNDARIES); - return(19); - } - else { - eg->layerbounds[eg->layers] = atoi(argv[arg+1]); - eg->layernumber[eg->layers] = atoi(argv[arg+2]); - eg->layerthickness[eg->layers] = atof(argv[arg+3]); - eg->layerratios[eg->layers] = atof(argv[arg+4]); - eg->layerparents[eg->layers] = 0; - eg->layers++; - } - } - - if(strcmp(argv[arg],"-layermove") == 0) { - if(arg+1 >= argc) { - printf("Give maximum number of Jacobi filters.\n"); - return(20); - } - else { - eg->layermove = atoi(argv[arg+1]); - } - } - - /* This uses a very dirty trick where the variables related to argument -layer are used - with a negative indexing */ - if(strcmp(argv[arg],"-divlayer") == 0) { - if(arg+4 >= argc) { - printf("Give four parameters for the layer: boundary, elements, relative thickness, ratio.\n"); - return(21); - } - else if(abs(eg->layers) == MAXBOUNDARIES) { - printf("There can only be %d layers, sorry.\n",MAXBOUNDARIES); - return(22); - } - else { - eg->layerbounds[abs(eg->layers)] = atoi(argv[arg+1]); - eg->layernumber[abs(eg->layers)] = atoi(argv[arg+2]); - eg->layerthickness[abs(eg->layers)] = atof(argv[arg+3]); - eg->layerratios[abs(eg->layers)] = atof(argv[arg+4]); - eg->layerparents[abs(eg->layers)] = 0; - eg->layers--; - } - } - - if(strcmp(argv[arg],"-3d") == 0) { - eg->dim = dim = 3; - } - if(strcmp(argv[arg],"-2d") == 0) { - eg->dim = dim = 2; - } - if(strcmp(argv[arg],"-1d") == 0) { - eg->dim = dim = 1; - } - - if(strcmp(argv[arg],"-isoparam") == 0) { - eg->isoparam = TRUE; - } - if(strcmp(argv[arg],"-nobound") == 0) { - eg->saveboundaries = FALSE; - } - - /* The following keywords are not actively used */ - - if(strcmp(argv[arg],"-map") ==0) { - if(arg+1 >= argc) { - printf("Give the name of the mapping file\n"); - return(23); - } - else { - strcpy(eg->mapfile,argv[arg+1]); - printf("Mapping file is %s\n",eg->mapfile); - } - } - if(strcmp(argv[arg],"-bcoffset") == 0) { - eg->bcoffset = atoi(argv[arg+1]); - } - if(strcmp(argv[arg],"-noelements") == 0) { - eg->elements3d = atoi(argv[arg+1]); - } - if(strcmp(argv[arg],"-nonodes") == 0) { - eg->nodes3d = atoi(argv[arg+1]); - } - - if(strcmp(argv[arg],"-sidefind") == 0) { - eg->findsides = 0; - for(i=arg+1;isidebulk[i-1-arg] = atoi(argv[i]); - eg->findsides++; - } - } - if(strcmp(argv[arg],"-findbound") == 0) { - eg->findsides = 0; - for(i=arg+1;i+1sidebulk[i-1-arg] = atoi(argv[i]); - eg->sidebulk[i-arg] = atoi(argv[i+1]); - eg->findsides++; - } - } - } - - { - char *ptr1; - ptr1 = strchr(eg->filesout[0], '.'); - if (ptr1) *ptr1 = '\0'; - ptr1 = strchr(eg->mapfile, '.'); - if (ptr1) *ptr1 = '\0'; - } - - return(0); -} - - #if EXE_MODE -static void Goodbye() -{ - printf("\nThank you for using Elmergrid!\n"); - printf("Send bug reports and feature wishes to peter.raback@csc.fi\n"); - exit(0); -} - -static void Instructions() -{ - printf("****************** Elmergrid ************************\n"); - printf("This program can create simple 2D structured meshes consisting of\n"); - printf("linear, quadratic or cubic rectangles or triangles. The meshes may\n"); - printf("also be extruded and revolved to create 3D forms. In addition many\n"); - printf("mesh formats may be imported into Elmer software. Some options have\n"); - printf("not been properly tested. Contact the author if you face problems.\n\n"); - - printf("The program has two operation modes\n"); - printf("A) Command file mode which has the command file as the only argument\n"); - printf(" 'ElmerGrid commandfile.eg'\n\n"); - - printf("B) Inline mode which expects at least three input parameters\n"); - printf(" 'ElmerGrid 1 3 test'\n\n"); - printf("The first parameter defines the input file format:\n"); - printf("1) .grd : Elmergrid file format\n"); - printf("2) .mesh.* : Elmer input format\n"); - printf("3) .ep : Elmer output format\n"); - printf("4) .ansys : Ansys input format\n"); - printf("5) .inp : Abaqus input format by Ideas\n"); - printf("6) .msh : Nastran format\n"); - printf("7) .FDNEUT : Gambit (Fidap) neutral file\n"); - printf("8) .unv : Universal mesh file format\n"); - printf("9) .mphtxt : Comsol Multiphysics mesh format\n"); - printf("10) .dat : Fieldview format\n"); - printf("11) .node,.ele: Triangle 2D mesh format\n"); - printf("12) .mesh : Medit mesh format\n"); - printf("13) .msh : GID mesh format\n"); - printf("14) .msh : Gmsh mesh format\n"); - printf("15) .ep.i : Partitioned ElmerPost format\n"); - - printf("\nThe second parameter defines the output file format:\n"); - printf("1) .grd : ElmerGrid file format\n"); - printf("2) .mesh.* : ElmerSolver format (also partitioned .part format)\n"); - printf("3) .ep : ElmerPost format\n"); - - printf("\nThe third parameter is the name of the input file.\n"); - printf("If the file does not exist, an example with the same name is created.\n"); - printf("The default output file name is the same with a different suffix.\n\n"); - - printf("There are several additional in-line parameters that are\n"); - printf("taken into account only when applicable to the given format.\n"); - - printf("-out str : name of the output file\n"); - printf("-in str : name of a secondary input file\n"); - printf("-silent : do not echo run time information\n"); - printf("-decimals : number of decimals in the saved mesh (eg. 8)\n"); - printf("-triangles : rectangles will be divided to triangles\n"); - printf("-relh real : give relative mesh density parameter for ElmerGrid meshing\n"); - printf("-merge real : merges nodes that are close to each other\n"); - printf("-order real[3] : reorder elements and nodes using c1*x+c2*y+c3*z\n"); - printf("-centralize : set the center of the mesh to origin\n"); - printf("-scale real[3] : scale the coordinates with vector real[3]\n"); - printf("-translate real[3] : translate the nodes with vector real[3]\n"); - printf("-rotate real[3] : rotate around the main axis with angles real[3]\n"); - printf("-clone int[3] : make ideantilcal copies of the mesh\n"); - printf("-clonesize real[3] : the size of the mesh to be cloned if larger to the original\n"); - printf("-unite : the meshes will be united\n"); - printf("-polar real : map 2D mesh to a cylindrical shell with given radius\n"); - printf("-cylinder : map 2D/3D cylindrical mesh to a cartesian mesh\n"); - printf("-reduce int[2] : reduce element order at material interval [int1 int2]\n"); - printf("-increase : increase element order from linear to quadratic\n"); - printf("-bcoffset int : add an offset to the boundary conditions\n"); - printf("-discont int : make the boundary to have secondary nodes\n"); - printf("-connect int : make the boundary to have internal connection among its elements\n"); - printf("-removelowdim : remove boundaries that are two ranks lower than highest dim\n"); - printf("-removeunused : remove nodes that are not used in any element\n"); - printf("-bulkorder : renumber materials types from 1 so that every number is used\n"); - printf("-boundorder : renumber boundary types from 1 so that every number is used\n"); - printf("-autoclean : this performs the united action of the three above\n"); - printf("-bulkbound int[3] : set the union of materials [int1 int2] to be boundary int3\n"); - printf("-boundbound int[3] : set the union of boundaries [int1 int2] to be boundary int3\n"); - printf("-bulktype int[3] : set material types in interval [int1 int2] to type int3\n"); - printf("-boundtype int[3] : set sidetypes in interval [int1 int2] to type int3\n"); - printf("-layer int[2] real[2]: make a boundary layer for given boundary\n"); - printf("-layermove int : apply Jacobi filter int times to move the layered mesh\n"); - printf("-divlayer int[2] real[2]: make a boundary layer for given boundary\n"); - printf("-3d / -2d / -1d : mesh is 3, 2 or 1-dimensional (applies to examples)\n"); - printf("-isoparam : ensure that higher order elements are convex\n"); - printf("-nobound : disable saving of boundary elements in ElmerPost format\n"); - - printf("\nThe following keywords are related only to the parallel Elmer computations.\n"); - printf("-partition int[4] : the mesh will be partitioned in main directions\n"); - printf("-partorder real[3] : in the above method, the direction of the ordering\n"); -#if HAVE_METIS - printf("-metis int[2] : the mesh will be partitioned with Metis\n"); -#endif - printf("-halo : create halo for the partitioning\n"); - printf("-indirect : create indirect connections in the partitioning\n"); - printf("-periodic int[3] : decleare the periodic coordinate directions for parallel meshes\n"); - printf("-saveinterval int[3] : the first, last and step for fusing parallel data\n"); - - if(0) printf("-names : conserve name information where applicable\n"); -} - - - - - - - static int PartitionMesh(int nofile) { /* Partititioning related stuff */ @@ -1007,7 +361,7 @@ int ConvertEgTypeToMeshType(struct FemType *dat,struct BoundaryType *bound,mesh_ printf("Implemented only for element dimensions 2 and 3 (not %d)\n",elemdim); } - printf("Done converting\n"); + printf("Done converting mesh\n"); return(0); } @@ -1054,6 +408,7 @@ static int ImportMeshDefinition(int inmethod,int nofile,char *filename,int *nogr *nogrids = 0; if(!visited) { + printf("Initializing structures for max. %d meshes\n",MAXCASES); for(k=0;k #include #include #include +#include #include "egutils.h" #include "egdef.h" #include "egtypes.h" -#include "egmesh.h" #include "egnative.h" +#include "egmesh.h" #define DEBUG 0 @@ -65,7 +67,9 @@ void GetElementInfo(int element,struct FemType *data, int GetElementDimension(int elementtype) { - int elemdim = 0; + int elemdim; + + elemdim = 0; switch (elementtype / 100) { case 1: @@ -91,6 +95,7 @@ int GetElementDimension(int elementtype) return(elemdim); } + int GetMaxElementType(struct FemType *data) { int i,maxelementtype; @@ -127,6 +132,37 @@ int GetMaxElementDimension(struct FemType *data) } +int GetCoordinateDimension(struct FemType *data,int info) +{ + int i,j,noknots,coorddim; + int coordis; + Real *coord; + Real epsilon = 1.0e-20; + + noknots = data->noknots; + coorddim = 0; + + for(j=3;j>=1;j--) { + coordis = FALSE; + if( j==1 ) + coord = data->x; + else if( j==2 ) + coord = data->y; + else + coord = data->z; + + for(i=1;i<=noknots;i++) + if( fabs( coord[i] ) > epsilon ) { + coordis = TRUE; + break; + } + if( coordis ) coorddim = MAX( coorddim, j ); + } + if(info) printf("Coordinates defined in %d dimensions\n",coorddim); + + return(coorddim); +} + void GetElementSide(int element,int side,int normal, struct FemType *data,int *ind,int *sideelemtype) @@ -136,15 +172,21 @@ void GetElementSide(int element,int side,int normal, elements. */ { - int i,j,elemtype,*elemind,sides,ind2[MAXNODESD2]; + int i,j,elemtype,*elemind=NULL,sides,ind2[MAXNODESD2]; + /* if(element < 1 || element > data->noelements ) { + printf("Invalid index for element: %d\n",element); + bigerror("Cannot continue"); + } */ + elemtype = data->elementtypes[element]; elemind = data->topology[element]; sides = elemtype/100; + *sideelemtype = 0; if(side < 0 && sides > 4) side = -(side+1); - + switch (elemtype) { case 202: case 203: @@ -343,7 +385,7 @@ void GetElementSide(int element,int side,int normal, break; - case 706: /* Linear prism or vedge element */ + case 706: /* Linear wedge element */ if(side < 3) { *sideelemtype = 404; ind[0] = elemind[side]; @@ -377,6 +419,45 @@ void GetElementSide(int element,int side,int normal, } break; + case 715: /* Quadratic wedge element */ + if(side < 3) { + *sideelemtype = 408; + ind[0] = elemind[side]; + ind[1] = elemind[(side+1)%3]; + ind[2] = elemind[(side+1)%3+3]; + ind[3] = elemind[side+3]; + ind[4] = elemind[6+side]; + ind[5] = elemind[12+(side+1)%3]; + ind[6] = elemind[9+side]; + ind[7] = elemind[12+side]; + } + else if (side < 5) { + *sideelemtype = 306; + for(i=0;i<3;i++) { + ind[i] = elemind[3*(side-3)+i]; + ind[i+3] = elemind[3*(side-3)+6+i]; + } + } + else if(side < 14) { + *sideelemtype = 202; + if(side < 8) { + ind[0] = elemind[side-5]; + ind[1] = elemind[(side-4)%3]; + } + if(side < 11) { + ind[0] = elemind[3+side-8]; + ind[1] = elemind[3+(side-7)%3]; + } + else { + ind[0] = elemind[side-11]; + ind[1] = elemind[3+side-11]; + } + } + else if (side < 20) { + *sideelemtype = 101; + ind[0] = elemind[side-14]; + } + break; case 605: /* Linear pyramid */ if(side < 4) { @@ -387,7 +468,7 @@ void GetElementSide(int element,int side,int normal, } else if (side < 5) { *sideelemtype = 404; - for(i=0;i<3;i++) + for(i=0;i<4;i++) ind[i] = elemind[i]; } else if(side < 13) { @@ -420,9 +501,9 @@ void GetElementSide(int element,int side,int normal, } else if (side == 4) { *sideelemtype = 408; - for(i=0;i<3;i++) + for(i=0;i<4;i++) ind[i] = elemind[i]; - for(i=0;i<3;i++) + for(i=0;i<4;i++) ind[i+4] = elemind[i+5]; } else if(side < 13) { @@ -480,8 +561,8 @@ void GetElementSide(int element,int side,int normal, break; case 820: /* 2nd order brick */ - *sideelemtype = 408; if(side < 4) { + *sideelemtype = 408; ind[0] = elemind[side]; ind[1] = elemind[(side+1)%4]; ind[2] = elemind[(side+1)%4+4]; @@ -492,6 +573,7 @@ void GetElementSide(int element,int side,int normal, ind[7] = elemind[12+side]; } else if(side < 6) { + *sideelemtype = 408; for(i=0;i<4;i++) ind[i] = elemind[4*(side-4)+i]; for(i=0;i<4;i++) @@ -500,8 +582,8 @@ void GetElementSide(int element,int side,int normal, break; case 827: - *sideelemtype = 409; if(side < 4) { + *sideelemtype = 409; ind[0] = elemind[side]; ind[1] = elemind[(side+1)%4]; ind[2] = elemind[(side+1)%4+4]; @@ -513,6 +595,7 @@ void GetElementSide(int element,int side,int normal, ind[8] = elemind[20+side]; } else { + *sideelemtype = 409; for(i=0;i<4;i++) ind[i] = elemind[4*(side-4)+i]; for(i=0;i<4;i++) @@ -523,6 +606,7 @@ void GetElementSide(int element,int side,int normal, default: printf("GetElementSide: unknown elementtype %d (elem=%d,side=%d)\n",elemtype,element,side); + bigerror("Cannot continue"); } if(normal == -1) { @@ -533,19 +617,99 @@ void GetElementSide(int element,int side,int normal, for(i=0;i<=j;i++) ind[i] = ind2[j-i]; } -#if 0 - else if(normal != 1) { - printf("GetElementSide: unknown option (normal=%d)\n",normal); + } +} + + + +void GetBoundaryElement(int sideind,struct BoundaryType *bound,struct FemType *data,int *ind,int *sideelemtype) +{ + int element,side,normal,i,n; + + if( sideind > bound->nosides ) { + *sideelemtype = 0; + printf("Side element index %d exceeds size of boundary (%d)\n",sideind,bound->nosides); + return; + } + + element = bound->parent[sideind]; + + + /*GetElementSide(elemind2,side,1,data,&sideind2[0],&sideelemtype2); */ + + if(element) { + side = bound->side[sideind]; + normal = bound->normal[sideind]; + + GetElementSide(element,side,normal,data,ind,sideelemtype); + } + else { + *sideelemtype = bound->elementtypes[sideind]; + + n = *sideelemtype % 100; + for(i=0;itopology[sideind][i]; + + if(0) { + printf("sidelemtype = %d\n",*sideelemtype); + printf("ind = "); + for(i=0;ielementtypes[element]; basetype = elemtype / 100; @@ -601,19 +765,19 @@ int GetElementGraph(int element,int edge,struct FemType *data,int *ind) } break; case 7: - if(side < 3) { - ind[0] = elemind[side]; - ind[1] = elemind[(side+1)%3]; - } - else if(side < 6) { - ind[0] = elemind[side-3]; - ind[1] = elemind[side]; - } - else if(side < 9) { - ind[0] = elemind[side-3]; - ind[1] = elemind[3+(side+1)%3]; + switch(side) { + case 0: ind[0]=elemind[0]; ind[1]=elemind[1]; break; + case 1: ind[0]=elemind[1]; ind[1]=elemind[2]; break; + case 2: ind[0]=elemind[2]; ind[1]=elemind[0]; break; + case 3: ind[0]=elemind[3]; ind[1]=elemind[4]; break; + case 4: ind[0]=elemind[4]; ind[1]=elemind[5]; break; + case 5: ind[0]=elemind[5]; ind[1]=elemind[3]; break; + case 6: ind[0]=elemind[0]; ind[1]=elemind[3]; break; + case 7: ind[0]=elemind[1]; ind[1]=elemind[4]; break; + case 8: ind[0]=elemind[2]; ind[1]=elemind[5]; break; } break; + case 8: if(side < 4) { ind[0] = elemind[side]; @@ -673,6 +837,7 @@ int GetElementGraph(int element,int edge,struct FemType *data,int *ind) + int CalculateIndexwidth(struct FemType *data,int indxis,int *indx) { int i,ind,nonodes,indexwidth; @@ -702,9 +867,6 @@ int CalculateIndexwidth(struct FemType *data,int indxis,int *indx) } - - - void InitializeKnots(struct FemType *data) { int i; @@ -713,26 +875,39 @@ void InitializeKnots(struct FemType *data) data->noknots = 0; data->noelements = 0; data->coordsystem = COORD_CART2; + data->numbering = NUMBER_XY; data->created = FALSE; data->variables = 0; data->maxnodes = 0; data->indexwidth = 0; data->noboundaries = 0; + data->mapgeo = 1; + data->nocorners = 0; data->boundarynamesexist = FALSE; data->bodynamesexist = FALSE; + data->nodepermexist = FALSE; + data->nopartitions = 1; data->partitionexist = FALSE; data->periodicexist = FALSE; - data->connectexist = FALSE; + data->nodeconnectexist = FALSE; + data->elemconnectexist = FALSE; - data->dualexists = FALSE; - data->invtopoexists = FALSE; + data->nodalexists = FALSE; + /* data->invtopoexists = FALSE; */ data->partitiontableexists = FALSE; + data->invtopo.created = FALSE; + data->nodalgraph2.created = FALSE; + data->dualgraph.created = FALSE; + + for(i=0;iedofs[i] = 0; + data->bandwidth[i] = 0; + data->iterdofs[i] = 0; strcpy(data->dofname[i],""); } @@ -1024,6 +1199,47 @@ static void MovePointPower(Real *lim,int points,Real *coords, } } +/* Creates airfoil shapes */ +static void MovePointNACAairfoil(Real *lim,int points,Real *coords, + Real x,Real y,Real *dx,Real *dy) +{ + Real p,d,t,u; + + if(y < lim[0] || y > lim[2]) return; + if(x < coords[0] || x > coords[1]) return; + + if(0) { + printf("x=%.3e y=%.3e lim0=%.3e lim2=%.3e\n",x,y,lim[0],lim[2]); + printf("naca: %.3e %.3e %.3e\n",coords[0],coords[1],coords[2]); + } + + t = x; + if(coords[1] > coords[0]) { + if(tcoords[1]) t = coords[1]; + } + else { + if(t>coords[0]) t = coords[0]; + if(tnocells = grid->nocells; data->noelements = grid->noelements; data->coordsystem = grid->coordsystem; + data->numbering = grid->numbering; data->indexwidth = grid->maxwidth; data->noknots = MAX(noknots,grid->noknots); @@ -1140,7 +1357,7 @@ void CreateKnots(struct GridType *grid,struct CellType *cell, maplim[3*k+2] = maplim[3*k+1] + grid->mappinglimits[2*k+1]; } - + mode = 0; if(grid->mappings) for(level=0;level<10;level++) { @@ -1194,6 +1411,10 @@ void CreateKnots(struct GridType *grid,struct CellType *cell, MovePointAngle(&maplim[3*k],grid->mappingpoints[k],grid->mappingparams[k], x,y,&dx,&dz); break; + case 9: + MovePointNACAairfoil(&maplim[3*k],grid->mappingpoints[k],grid->mappingparams[k], + x,y,&dx,&dy); + break; case -1: @@ -1228,6 +1449,10 @@ void CreateKnots(struct GridType *grid,struct CellType *cell, MovePointAngle(&maplim[3*k],grid->mappingpoints[k],grid->mappingparams[k], y,x,&dy,&dz); break; + case -9: + MovePointNACAairfoil(&maplim[3*k],grid->mappingpoints[k],grid->mappingparams[k], + y,x,&dy,&dx); + break; } @@ -1269,7 +1494,7 @@ void CreateKnots(struct GridType *grid,struct CellType *cell, data->maxsize = sqrt(maxsize); data->minsize = sqrt(minsize); - if(info) printf("Maximum elementsize is %.3le and minimum %.3le.\n", + if(info) printf("Maximum elementsize is %.3e and minimum %.3e.\n", data->maxsize,data->minsize); } @@ -1277,7 +1502,7 @@ void CreateKnots(struct GridType *grid,struct CellType *cell, int CreateVariable(struct FemType *data,int variable,int unknowns, - Real value, const char *dofname,int eorder) + Real value,const char *dofname,int eorder) /* Create variables for the given data structure */ { int i,info=FALSE; @@ -1297,11 +1522,13 @@ int CreateVariable(struct FemType *data,int variable,int unknowns, data->variables += 1; data->edofs[variable] = unknowns; data->alldofs[variable] = unknowns * data->noknots; + data->bandwidth[variable] = unknowns * data->indexwidth; data->dofs[variable] = Rvector(1,timesteps * data->alldofs[variable]); if(info) printf("Created variable %s with %d dofs.\n", dofname,data->alldofs[variable]); for(i=1;i<=data->alldofs[variable]*timesteps;i++) data->dofs[variable][i] = value; + data->iterdofs[variable] = 1; } else if (data->edofs[variable] == unknowns) { if(info) printf("CreateVariable: Variable %d exists with correct number of dofs!\n", @@ -1313,6 +1540,17 @@ int CreateVariable(struct FemType *data,int variable,int unknowns, return(2); } + + if(eorder) { + if (data->eorder[variable] == FALSE) { + data->eorder[variable] = TRUE; + data->order[variable] = Ivector(1,data->alldofs[variable]); + for(i=1;i<=data->alldofs[variable];i++) + data->order[variable][i] = i; + } + if(info) printf("Created index for variable %s.\n",dofname); + } + strcpy(data->dofname[variable],dofname); return(0); @@ -1341,128 +1579,17 @@ void DestroyKnots(struct FemType *data) free_Rvector(data->x,1,data->noknots); free_Rvector(data->y,1,data->noknots); free_Rvector(data->z,1,data->noknots); - + data->noknots = 0; data->noelements = 0; data->maxnodes = 0; -} - - - -int FindParentSide(struct FemType *data,struct BoundaryType *bound, - int sideelem,int sideelemtype,int *sideind) -{ - int i,j,sideelemtype2,elemind,parent,normal; - int elemsides = 0,side,sidenodes,nohits,hit,noparent, bulknodes; - int sideind2[MAXNODESD1]; - - hit = FALSE; - - for(parent=1;parent<=2;parent++) { - if(parent == 1) { - elemind = bound->parent[sideelem]; - noparent = (parent < 1); - } - else - elemind = bound->parent2[sideelem]; - - if(elemind > 0) { - elemsides = data->elementtypes[elemind] / 100; - bulknodes = data->elementtypes[elemind] % 100; - - if(elemsides == 8) elemsides = 6; - else if(elemsides == 6) elemsides = 5; - else if(elemsides == 5) elemsides = 4; - - for(normal=1;normal >= -1;normal -= 2) { - - for(side=0;side 300) break; - if(sideelemtype2 < 200 && sideelemtype > 200) break; - if(sideelemtype != sideelemtype2) continue; - - sidenodes = sideelemtype % 100; - - for(j=0;jside[sideelem] = side; - bound->normal[sideelem] = normal; - } - else { - bound->side2[sideelem] = side; - } - goto skip; - } - } - } - } - - - /* this finding of sides does not guarantee that normals are oriented correctly */ - normal = 1; - hit = FALSE; - - for(side=0;;side++) { - - GetElementSide(elemind,side,normal,data,&sideind2[0],&sideelemtype2); - - if(sideelemtype2 < 300 && sideelemtype > 300) break; - if(sideelemtype2 < 200 && sideelemtype > 200) break; - if(sideelemtype != sideelemtype2) continue; - - sidenodes = sideelemtype % 100; - - nohits = 0; - for(j=0;jside[sideelem] = side; - } - else - bound->side2[sideelem] = side; - goto skip; - } - - } - } - - skip: - if(!hit) { - printf("FindParentSide: unsuccessful (elemtype=%d elemsides=%d parent=%d)\n", - sideelemtype,elemsides,parent); - - printf("parents = %d %d\n",bound->parent[sideelem],bound->parent2[sideelem]); - - printf("sideind ="); - for(i=0;itopology[elemind][i]); - printf("\n"); - } - - } - return(0); + if(data->nocorners > 0) + free_Ivector(data->corners,1,2*data->nocorners); } - int CreateBoundary(struct CellType *cell,struct FemType *data, struct BoundaryType *bound,int material1,int material2, int solidmat,int boundarytype,int info) @@ -1477,65 +1604,69 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, by the flag 'solidmat'. */ { - int side,more,elem,elemind[2],nosides,no,times; + int i,side,more,elem,elemind[2],nosides,no,times; int sidemat,thismat,size,setpoint,dimsides,cellside; if(data->dim == 1) dimsides = 2; else dimsides = 4; - + if(bound->created == TRUE) { - printf("CreateBoundary: You tried to recreate the boundary!\n"); + if(info) printf("CreateBoundary: You tried to recreate the boundary!\n"); return(1); } if(!data->created) { - printf("CreateBoundary: You tried to create a boundary before the knots were made."); + if(info) printf("CreateBoundary: You tried to create a boundary before the knots were made."); return(2); } if(material1 < 0 && material2 < 0) { - printf("CreateBoundary: the material arguments are both negative"); + if(info) printf("CreateBoundary: the material arguments are both negative"); return(3); } - + times = 0; - + bound->created = FALSE; bound->nosides = 0; if(solidmat >= 2) solidmat -= 2; - - startpoint: - + +startpoint: + /* Go through all elements which have a boundary with the given material, but are not themself of that material. First only calculate their amount, then allocate space and tabulate them. */ nosides = 0; - - + + for(no=1; no <= data->nocells; no++) for(side=0; side < dimsides; side++) { - + if(data->dim == 1) cellside = 3-2*side; else cellside = side; - + setpoint = FALSE; sidemat = cell[no].boundary[cellside]; thismat = cell[no].material; - + /* The free boundary conditions are not allowed if the negative keywords are used. */ - + /* Either material must be the one defined. */ if( material1 >= 0 && material1 != sidemat) continue; if( material2 >= 0 && material2 != thismat) continue; - +#if 0 + printf("mat=[%d %d] sidemat=%d thismat=%d side=%d\n", + material1,material2,sidemat,thismat,side); +#endif + if( material2 == -((side+2)%4+1) && sidemat == material1 && sidemat != thismat) setpoint = TRUE; if( material1 == -(side+1) && thismat == material2 && sidemat != thismat) setpoint = TRUE; - + if( material1 == MAT_BIGGER && sidemat > material2 ) setpoint = TRUE; if( material1 == MAT_SMALLER && sidemat < material2 ) setpoint = TRUE; if( material1 == MAT_ANYTHING && sidemat != material2 ) setpoint = TRUE; @@ -1543,28 +1674,35 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, if( material2 == MAT_SMALLER && thismat < material1 ) setpoint = TRUE; if( material2 == MAT_ANYTHING && thismat != material1 ) setpoint = TRUE; if( sidemat == material1 && thismat == material2 ) setpoint = TRUE; - + if(setpoint == TRUE) { - +#if 0 + printf("going through boundary %d vs. %d in cell %d\n",material1,material2,no); +#endif elem = 0; do { elem++; nosides++; more = GetSideInfo(cell,no,side,elem,elemind); - + +#if 0 + printf("elem=%d nosides=%d no=%d side=%d elemind=%d %d\n", + elem,nosides, no, side, elemind[0], elemind[1]); +#endif + /* In the second round the values are tabulated. */ if(times) { /* It is assumed that the material pointed by solidmat determines the surface properties. */ - + bound->parent[nosides] = elemind[0]; bound->parent2[nosides] = elemind[1]; - + bound->side[nosides] = side; bound->side2[nosides] = (side+dimsides/2)%dimsides; - + bound->types[nosides] = boundarytype; - + /* The direction of the surface normal must be included */ if(solidmat==FIRST) { bound->material[nosides] = sidemat; @@ -1579,11 +1717,11 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, } while(more); } - } - + } + if(nosides == 0) { - printf("No boundary between materials %d and %d exists.\n", - material1,material2); + if(info) printf("No boundary between materials %d and %d exists.\n", + material1,material2); return(0); } @@ -1603,6 +1741,8 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, bound->parent = Ivector(1,nosides); bound->parent2 = Ivector(1,nosides); bound->normal = Ivector(1,nosides); + + bound->echain = FALSE; bound->ediscont = FALSE; goto startpoint; @@ -1610,170 +1750,11 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, if(info) printf("%d element sides between materials %d and %d were located to type %d.\n", nosides,material1,material2,boundarytype); - return(0); -} - - - -int CreateAllBoundaries(struct CellType *cell,struct FemType *data, - struct BoundaryType *bound,int info) -/* This subroutine creates all available boundaries */ -{ - int i,j,side,more,elem,elemind[2],nosides,no,times; - int sidemat,thismat,size,setpoint,dimsides,cellside; - int boundarytype,prevsidemat,prevthismat; - int **bctypes,minmat,maxmat,maxtype; - - - if(data->dim == 1) - dimsides = 2; - else - dimsides = 4; - - if(bound->created == TRUE) { - printf("CreateBoundary: You tried to recreate the boundary!\n"); - return(1); - } - if(!data->created) { - printf("CreateBoundary: You tried to create a boundary before the knots were made."); - return(2); - } - - times = 0; - - bound->created = FALSE; - bound->nosides = 0; - - - maxmat = minmat = 0; - - for(no=1; no <= data->nocells; no++) { - for(side=0; side < dimsides; side++) { - if(data->dim == 1) - cellside = 3-2*side; - else - cellside = side; - - sidemat = cell[no].boundary[cellside]; - thismat = cell[no].material; - - if(maxmat = 0) { - maxmat = thismat; - minmat = thismat; - } - maxmat = MAX(maxmat,thismat); - maxmat = MAX(maxmat,sidemat); - minmat = MIN(minmat,thismat); - minmat = MIN(minmat,sidemat); - } - } - - bctypes = Imatrix(minmat,maxmat,minmat,maxmat); - for(i=minmat;i<=maxmat;i++) - for(j=minmat;j<=maxmat;j++) - bctypes[i][j] = 0; - - boundarytype = 0; - for(no=1; no <= data->nocells; no++) { - for(side=0; side < dimsides; side++) { - if(data->dim == 1) - cellside = 3-2*side; - else - cellside = side; - - sidemat = cell[no].boundary[cellside]; - thismat = cell[no].material; - - if(sidemat == thismat) continue; - - if(bctypes[thismat][sidemat] == 0) { - boundarytype += 1; - bctypes[thismat][sidemat] = boundarytype; - if(0) printf("type[%d %d] = %d\n",thismat,sidemat,boundarytype); - } - } - } - maxtype = boundarytype; - - - -startpoint: - /* Go through all elements which have a boundary with the given material, but - are not themself of that material. First only calculate their amount, then - allocate space and tabulate them. */ - nosides = 0; - - for(no=1; no <= data->nocells; no++) - for(side=0; side < dimsides; side++) { - - if(data->dim == 1) - cellside = 3-2*side; - else - cellside = side; - - setpoint = FALSE; - sidemat = cell[no].boundary[cellside]; - thismat = cell[no].material; - - if(sidemat == thismat) continue; - boundarytype = bctypes[thismat][sidemat]; - - elem = 0; - do { - elem++; - nosides++; - more = GetSideInfo(cell,no,side,elem,elemind); - - /* In the second round the values are tabulated. */ - if(times) { - bound->parent[nosides] = elemind[0]; - bound->parent2[nosides] = elemind[1]; - - bound->side[nosides] = side; - bound->side2[nosides] = (side+dimsides/2)%dimsides; - - bound->types[nosides] = boundarytype; - bound->normal[nosides] = 1; - } - } while(more); - - prevsidemat = sidemat; - prevthismat = thismat; - } - - if(nosides == 0) return(0); - - if(times == 0) { - times++; - - /* Allocate space. This has sometimes led to strange errors. - The allocation takes place only in the first loop. */ - - bound->created = TRUE; - bound->nosides = size = nosides; - bound->coordsystem = data->coordsystem; - bound->types = Ivector(1,nosides); - bound->side = Ivector(1,nosides); - bound->side2 = Ivector(1,nosides); - bound->material = Ivector(1,nosides); - bound->parent = Ivector(1,nosides); - bound->parent2 = Ivector(1,nosides); - bound->normal = Ivector(1,nosides); - bound->ediscont = FALSE; - - goto startpoint; - } - - free_Imatrix(bctypes,minmat,maxmat,minmat,maxmat); - - if(info) printf("%d boundary elements with %d types were automatically created\n",nosides,maxtype); return(0); } - - int AllocateBoundary(struct BoundaryType *bound,int size) { int i; @@ -1785,6 +1766,7 @@ int AllocateBoundary(struct BoundaryType *bound,int size) bound->created = TRUE; bound->nosides = size; + bound->echain = FALSE; bound->ediscont = FALSE; bound->material = Ivector(1,size); @@ -1810,13 +1792,10 @@ int AllocateBoundary(struct BoundaryType *bound,int size) - - - int DestroyBoundary(struct BoundaryType *bound) /* Destroys boundaries of various types. */ { - int nosides; + int i,nosides; if(!bound->created) { return(1); @@ -1846,12 +1825,35 @@ int DestroyBoundary(struct BoundaryType *bound) +int CreateBoundaries(struct CellType *cell,struct FemType *data, + struct BoundaryType *boundaries,int info) +{ + int i,j; + + j = 0; + if(data->noboundaries > 0) + for(i=0;inoboundaries;i++) { + while(boundaries[j].created) { + j++; + if(j >= MAXBOUNDARIES) { + printf("CreateBoundaries: too many boundaries %d\n",j); + return(1); + } + } + CreateBoundary(cell,data,&boundaries[j], + data->boundext[i],data->boundint[i], + data->boundsolid[i],data->boundtype[i],info); + } + return(0); +} + + int CreatePoints(struct CellType *cell,struct FemType *data, struct BoundaryType *bound, int param1,int param2,int pointmode,int pointtype,int info) { - int size,i,no,corner,times,elem = 0,node; + int size,i,no,corner,times,elem,node; bound->created = FALSE; bound->nosides = 0; @@ -1922,7 +1924,7 @@ int CreatePoints(struct CellType *cell,struct FemType *data, bound->parent[i] = elem; node = data->topology[elem][corner]; - printf("Found node %d at (%.3lg, %.3lg)\n",node,data->x[node],data->y[node]); + if(info) printf("Found node %d at (%.3lg, %.3lg)\n",node,data->x[node],data->y[node]); } } @@ -1940,17 +1942,18 @@ int CreatePoints(struct CellType *cell,struct FemType *data, } -static int CreateNewNodes(struct FemType *data,int *order,int material,int newknots, - int info) + +int CreateNewNodes(struct FemType *data,int *order,int material,int newknots) { int i,j,k,l,lmax,ind; int newsize,noknots,nonodes; int *neworder; - Real *newx,*newy,*newz,*newdofs[MAXDOFS]; + Real *newx=NULL,*newy=NULL,*newz=NULL; + Real *newdofs[MAXDOFS]; noknots = data->noknots; - if(info) printf("Creating %d new nodes for discoutinuous boundary.\n",newknots); + printf("Creating %d new nodes for discontinuous boundary.\n",newknots); /* Allocate for the new nodes */ newsize = noknots+newknots; @@ -1985,6 +1988,7 @@ static int CreateNewNodes(struct FemType *data,int *order,int material,int newkn newx[j] = data->x[i]; newy[j] = data->y[i]; newz[j] = data->z[i]; + for(k=1;kedofs[k]) for(l=1;l<=lmax;l++) @@ -2046,11 +2050,12 @@ int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, */ { int i,j,bc,ind,sideind[MAXNODESD1]; - int side,parent,newknots,doublesides,maxtype,newbc; + int side,parent,newnodes,doublesides,maxtype,newbc; int newsuccess,noelements,nonodes,sideelemtype,sidenodes,disconttype; - int *order; + int *order=NULL; int mat1,mat2,par1,par2,mat1old,mat2old,material; - static int hitsexist=FALSE,hitslength,*hits; + static int hitsexist=FALSE,hitslength,*hits=NULL; + if(boundtype < 0) { newbc = TRUE; @@ -2063,6 +2068,7 @@ int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, mat1old = mat2old = 0; doublesides = 0; + /* Compute the number of duplicate boundary elements */ for(bc=0;bc 0) material = mat1old; - else if(mat2old > 0) material = mat2old; + if( mat1old > 0 && mat2old > 0 ) + material = MIN( mat1old, mat2old ); + else if(mat1old > 0) + material = mat1old; + else if(mat2old > 0) + material = mat2old; else { printf("SetDiscontinuousBoundary: impossible to make the boundary of several materials\n"); return(2); } + if(info) { + printf("Creating discontinuous boundary between materials %d and %d\n",mat1old,mat2old); + printf("New set of nodes will be created for material %d\n",material); + } + + noelements = data->noelements; order = Ivector(1,data->noknots); for(i=1;i<=data->noknots;i++) order[i] = i; + /* Compute the endnodes by the fact that they have different number of + hits */ if(endnodes == 1) { if(!hitsexist) { - hitslength = (int) (1.1*data->noknots); + hitslength = 1.1*data->noknots; hits = Ivector(1,hitslength); hitsexist = TRUE; } else if(hitslength <= data->noknots) { free_Ivector(hits,1,hitslength); - hitslength = (int) (1.1*data->noknots); + hitslength = 1.1*data->noknots; hits = Ivector(1,hitslength); } @@ -2123,7 +2139,7 @@ int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, } } - + /* If requested create a secondary boundary at the other side */ if(newbc) { maxtype = 0; for(bc=0;bc 0) { - newknots++; - order[ind] = -newknots; + newnodes++; + order[ind] = -newnodes; } } else if(endnodes == 0) { if(order[ind] > 0) order[ind] = 0; else if(order[ind] == 0) { - newknots++; - order[ind] = -newknots; + newnodes++; + order[ind] = -newnodes; } } else if(endnodes == 1) { if(order[ind] > 0) { if(hits[ind] < 4) { - newknots++; - order[ind] = -newknots; + newnodes++; + order[ind] = -newnodes; } else order[ind] = 0; } else if(order[ind] == 0) { - newknots++; - order[ind] = -newknots; + newnodes++; + order[ind] = -newnodes; } } @@ -2207,9 +2223,9 @@ int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, } } - if(newknots == 0) return(3); + if(newnodes == 0) return(3); - newsuccess = CreateNewNodes(data,order,material,newknots,info); + newsuccess = CreateNewNodes(data,order,material,newnodes); return(newsuccess); } @@ -2304,26 +2320,43 @@ int FindPeriodicBoundary(struct FemType *data,struct BoundaryType *bound, -int SetConnectedBoundary(struct FemType *data,struct BoundaryType *bound, - int bctype,int connecttype,int info) -/* Create connected boundary conditions for a given bctype */ +int SetConnectedNodes(struct FemType *data,struct BoundaryType *bound, + int bctype,int connecttype,int info) +/* Mark node that are related to a boundary condition of a given bctype. + This may be used to create strong connections in the partitioning process. */ { - int i,j,k,bc,sideelemtype,sidenodes; - int sideind[MAXNODESD1]; + int i,j,k,bc,sideelemtype,sidenodes,nodesset; + int sideind[MAXNODESD1],conflicts; + + conflicts = 0; + nodesset = 0; - for(bc=0;bcconnectexist) { - data->connect = Ivector(1,data->noknots); + if( bctype > 0 ) { + if(bound[bc].types[i] != bctype) continue; + } + else if( bctype == -1 ) { + if( !bound[bc].parent[i] ) continue; + } + else if( bctype == -2 ) { + if( !bound[bc].parent[i] ) continue; + if( !bound[bc].parent2[i] ) continue; + } + else if( bctype == -3 ) { + if( !bound[bc].parent[i] ) continue; + if( bound[bc].parent2[i] ) continue; + } + + /* If the table pointing the connected nodes does not exist, create it */ + if(!data->nodeconnectexist) { + data->nodeconnect = Ivector(1,data->noknots); for(k=1;k<=data->noknots;k++) - data->connect[k] = 0; - data->connectexist = TRUE; + data->nodeconnect[k] = 0; + data->nodeconnectexist = TRUE; } GetElementSide(bound[bc].parent[i],bound[bc].side[i],bound[bc].normal[i], @@ -2332,7 +2365,77 @@ int SetConnectedBoundary(struct FemType *data,struct BoundaryType *bound, for(j=0;jconnect[k] = connecttype; + if( data->nodeconnect[k] != connecttype ) { + if( data->nodeconnect[k] ) conflicts += 1; + data->nodeconnect[k] = connecttype; + nodesset += 1; + } + } + } + } + if(info) printf("Setting connectivity group %d for %d nodes on boundary %d\n", + connecttype,nodesset,bctype); + + if(conflicts) printf("The were %d conflicts in the connectivity set %d\n", + conflicts,connecttype); + + return(0); +} + + +int SetConnectedElements(struct FemType *data,int info) +/* Create connected boundary conditions for a given bctype */ +{ + int i,j,k,nonodes,hit,nohits,con; + int *nodeconnect; + + if(!data->nodeconnectexist) { + printf("Cannot create connected elements without connected nodes!\n"); + return(1); + } + nodeconnect = data->nodeconnect; + + /* Allocated space for the connected elements */ + if(!data->elemconnectexist) { + printf("Created table for connected elements\n"); + data->elemconnect = Ivector(1,data->noelements); + for(k=1;k<=data->noelements;k++) + data->elemconnect[k] = 0; + data->elemconnectexist = TRUE; + + /* Go through all the elements and check which of the elements have + nodes that are related to a connected node */ + nohits = 0; + for(i=1;i<=data->noelements;i++) { + nonodes = data->elementtypes[i] % 100; + hit = FALSE; + for(j=0;jtopology[i][j]; + con = nodeconnect[k]; + if( con ) { + data->elemconnect[i] = MAX( con, data->elemconnect[i] ); + hit = TRUE; + } + } + if(hit) nohits++; + } + + if(info) printf("Number of connected elements is %d (out of %d)\n",nohits,data->noelements); + data->elemconnectexist = nohits; + } + + /* This is a little bit dirty. We set the connections to negative and use the unconnected + as a permutation. */ + if( data->elemconnectexist ) { + if(info) printf("Use connected table as a permutation for creating dual graph!\n"); + j = 0; + for(i=1;i<=data->noelements;i++) { + if( data->elemconnect[i] ) { + data->elemconnect[i] = -abs(data->elemconnect[i]); + } + else { + j++; + data->elemconnect[i] = j; } } } @@ -2342,15 +2445,84 @@ int SetConnectedBoundary(struct FemType *data,struct BoundaryType *bound, +int FindCorners(struct GridType *grid,struct CellType *cell, + struct FemType *data,int info) +/* Find the nodes in the mesh that are at material corners. + These nodes are often of special interest. + */ +{ + int i,j,k,ind,cellno,elem; + int allocated,nocorners; + + nocorners = 0; + allocated = FALSE; + +omstart: + + if(nocorners > 0) { + data->corners = Ivector(1,2*nocorners); + data->nocorners = nocorners; + allocated = TRUE; + } + + k = 0; + + for(i=1;i<=grid->xcells+1;i++) + for(j=1;j<=grid->ycells+1;j++) { + if(grid->structure[j][i] == grid->structure[j][i-1] && + grid->structure[j-1][i] == grid->structure[j-1][i-1]) + continue; + if(grid->structure[j][i] == grid->structure[j-1][i] && + grid->structure[j][i-1] == grid->structure[j-1][i-1]) + continue; + + /* point (i,j) must now be a corner */ + if(cellno = grid->numbered[j][i]) { + elem = GetElementIndex(&(cell)[cellno],1,1); + ind = BOTLEFT; + } + else if(cellno = grid->numbered[j][i-1]) { + elem = GetElementIndex(&(cell)[cellno],cell[cellno].xelem,1); + ind = BOTRIGHT; + } + else if(cellno = grid->numbered[j-1][i]) { + elem = GetElementIndex(&(cell)[cellno],1,cell[cellno].yelem); + ind = TOPLEFT; + } + else if(cellno = grid->numbered[j-1][i-1]) { + elem = GetElementIndex(&(cell)[cellno],cell[cellno].xelem,cell[cellno].yelem); + ind = TOPRIGHT; + } + else continue; + + /* ind is now the index of the corner knot */ + k++; + + if(allocated == FALSE) continue; + data->corners[2*k-1] = elem; + data->corners[2*k] = ind; + + } + + nocorners = k; + + if(nocorners == 0) return(0); + if(allocated == FALSE) goto omstart; + + if(info) printf("Found %d material corners.\n",nocorners); + return(0); +} + int ElementsToTriangles(struct FemType *data,struct BoundaryType *bound, Real critangle,int info) /* Make triangles out of rectangular elements */ { - int i,j,k,l,side = 0,elem,i1,isum = 0,sideelemtype; + int i,j,k,l,side,elem,i1,isum,sideelemtype; int noelements,elementtype,triangles,noknots,nonodes,newelements,newtype,newmaxnodes; - int **newtopo = NULL,*newmaterial = NULL,*newelementtypes = NULL,newnodes,*needed,*divisions,*division1; + int **newtopo=NULL,*newmaterial=NULL,*newelementtypes=NULL; + int newnodes,*needed=NULL,*divisions=NULL,*division1=NULL; int sideind[MAXNODESD1], sideind2[MAXNODESD1]; int allocated,maxanglej,evenodd,newelem; Real dx1,dx2,dy1,dy2,ds1,ds2; @@ -2396,14 +2568,13 @@ int ElementsToTriangles(struct FemType *data,struct BoundaryType *bound, dy2 = data->y[data->topology[i][(j+1)%4]] - data->y[data->topology[i][j]]; ds1 = sqrt(dx1*dx1+dy1*dy1); ds2 = sqrt(dx2*dx2+dy2*dy2); - angles[j] = (180.0/FM_PI) * acos((dx1*dx2+dy1*dy2)/(ds1*ds2)); - // angles[j] = (180.0/M_PI) * acos((dx1*dx2+dy1*dy2)/(ds1*ds2)); + angles[j] = (180.0/M_PI) * acos((dx1*dx2+dy1*dy2)/(ds1*ds2)); /* Slightly favor divisions where corner is split */ if(needed[data->topology[i][j]] == 1) angles[j] *= 1.001; if( abs(angles[j] > maxangle)) { - maxangle = fabs(angles[j]); + maxangle = abs(angles[j]); maxanglej = j; } } @@ -2601,6 +2772,8 @@ int ElementsToTriangles(struct FemType *data,struct BoundaryType *bound, continue; } + isum = 0; + side = 0; if(divisions[k] == 1) { elem = division1[k]+1; side = bound[j].side[i]; @@ -2712,13 +2885,16 @@ int CylinderCoordinates(struct FemType *data,int info) int UniteMeshes(struct FemType *data1,struct FemType *data2, struct BoundaryType *bound1,struct BoundaryType *bound2, - int info) + int nooverlap, int info) /* Unites two meshes for one larger mesh */ { int i,j,k; - int noelements,noknots,nonodes; - int **newtopo,*newmaterial,*newelementtypes,maxnodes; - Real *newx,*newy,*newz; + int noelements,noknots,nonodes,maxnodes; + int **newtopo=NULL,*newmaterial=NULL,*newelementtypes=NULL; + Real *newx=NULL,*newy=NULL,*newz=NULL; + int mat,usenames,*bodynameis,*boundarynameis,*bodyused,*boundaryused; + int bcmax1,bcmin2,bcoffset; + int bodymax1,bodymin2,bodyoffset; noknots = data1->noknots + data2->noknots; noelements = data1->noelements + data2->noelements; @@ -2728,78 +2904,238 @@ int UniteMeshes(struct FemType *data1,struct FemType *data2, if(0) printf("Uniting two meshes to %d nodes and %d elements.\n",noknots,noelements); - for(j=0;j < MAXBOUNDARIES;j++) { - if(!bound2[j].created) continue; + usenames = data1->bodynamesexist || data1->boundarynamesexist; + bcoffset = 0; bodyoffset = 0; - for(k=j;k < MAXBOUNDARIES;k++) - if(!bound1[k].created) break; + if( usenames ) { + bodynameis = Ivector(1,MAXBODIES); + boundarynameis = Ivector(1,MAXBCS); + bodyused = Ivector(1,MAXBODIES); + boundaryused = Ivector(1,MAXBCS); -#if 0 - printf("k=%d j=%d\n",k,j); -#endif - - bound1[k].created = bound2[j].created; - bound1[k].nosides = bound2[j].nosides; - bound1[k].coordsystem = bound2[j].coordsystem; - bound1[k].side = bound2[j].side; - bound1[k].side2 = bound2[j].side2; - bound1[k].parent = bound2[j].parent; - bound1[k].parent2 = bound2[j].parent2; - bound1[k].material = bound2[j].material; - bound1[k].types = bound2[j].types; - bound1[k].normal = bound2[j].normal; + for(i=1;i<=MAXBODIES;i++) + bodynameis[i] = bodyused[i] = FALSE; + for(i=1;i<=MAXBCS;i++) + boundarynameis[i] = boundaryused[i] = FALSE; - bound2[j].created = FALSE; - bound2[j].nosides = 0; + /* First mark the original bodies and boundaries that maintain their index */ + for(i=1;i<=data1->noelements;i++) { + mat = data1->material[i]; + if( mat < MAXBODIES ) { + if(!bodynameis[mat]) { + bodynameis[mat] = -1; + bodyused[mat] = TRUE; + } + } + } - for(i=1; i <= bound1[k].nosides; i++) { - bound1[k].parent[i] += data1->noelements; - if(bound1[k].parent2[i]) - bound1[k].parent2[i] += data1->noelements; + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound1[j].created) continue; + for(i=1; i <= bound1[k].nosides; i++) { + mat = bound1[j].types[i]; + if( mat < MAXBCS ) { + if(!boundarynameis[mat]) { + boundarynameis[mat] = -1; + boundaryused[mat] = TRUE; + } + } + } } - } - data1->maxnodes = maxnodes; - newtopo = Imatrix(1,noelements,0,maxnodes-1); - newmaterial = Ivector(1,noelements); - newelementtypes = Ivector(1,noelements); - newx = Rvector(1,noknots); - newy = Rvector(1,noknots); - newz = Rvector(1,noknots); - for(i=1;i<=data1->noknots;i++) { - newx[i] = data1->x[i]; - newy[i] = data1->y[i]; - newz[i] = data1->z[i]; - } - for(i=1;i<=data2->noknots;i++) { - newx[i+data1->noknots] = data2->x[i]; - newy[i+data1->noknots] = data2->y[i]; - newz[i+data1->noknots] = data2->z[i]; - } + /* Then mark the joined bodies and boundaries that are not conflicting */ + for(i=1;i<=data2->noelements;i++) { + mat = data2->material[i]; + if( mat < MAXBODIES ) { + if( !bodynameis[mat] ) { + bodynameis[mat] = mat; + bodyused[mat] = TRUE; + strcpy(data1->bodyname[mat],data2->bodyname[mat]); + } + } + } + + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound2[j].created) continue; + + for(i=1; i <= bound2[j].nosides; i++) { + mat = bound2[j].types[i]; + if( mat < MAXBCS ) { + if( !boundarynameis[mat] ) { + boundarynameis[mat] = mat; + boundaryused[mat] = TRUE; + strcpy(data1->boundaryname[mat],data2->boundaryname[mat]); + } + } + } + } + + + /* And finally number the conflicting joinded bodies and BCs */ + for(i=1;i<=data2->noelements;i++) { + mat = data2->material[i]; + if( mat < MAXBODIES ) { + if( bodynameis[mat] == -1) { + for(k=1;kbodyname[k],data2->bodyname[mat]); + } + } + } + + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound2[j].created) continue; + for(i=1; i <= bound2[j].nosides; i++) { + mat = bound2[j].types[i]; + + if( mat < MAXBCS ) { + if( boundarynameis[mat] == -1) { + for(k=1;kboundaryname[k],data2->boundaryname[mat]); + } + } + } + } + + } + else if (nooverlap ) { + bcmax1 = 0; + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound1[j].created) continue; + for(i=1; i <= bound1[k].nosides; i++) { + mat = bound1[j].types[i]; + bcmax1 = MAX( bcmax1, mat ); + } + } + + bcmin2 = 1000; + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound2[j].created) continue; + + for(i=1; i <= bound2[j].nosides; i++) { + mat = bound2[j].types[i]; + bcmin2 = MIN( bcmin2, mat ); + } + } + bcoffset = MAX(0, bcmax1 - bcmin2 + 1); + if( info ) { + printf("Max(bc1) is %d and Min(bc2) is %d, using BC offset %d for mesh 2!\n",bcmax1,bcmin2,bcoffset); + } + + bodymax1 = 0; + for(i=1;i<=data1->noelements;i++) { + mat = data1->material[i]; + bodymax1 = MAX( bodymax1, mat ); + } + + bodymin2 = 1000; + for(i=1;i<=data2->noelements;i++) { + mat = data2->material[i]; + bodymin2 = MIN( bodymin2, mat ); + } + bodyoffset = MAX(0, bodymax1 - bodymin2 + 1); + if( info ) { + printf("Max(body1) is %d and Min(body2) is %d, using body offset %d for mesh 2!\n",bodymax1,bodymin2,bodyoffset); + } + } + + + + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound2[j].created) continue; + + for(k=j;k < MAXBOUNDARIES;k++) + if(!bound1[k].created) break; + + bound1[k].created = bound2[j].created; + bound1[k].nosides = bound2[j].nosides; + bound1[k].coordsystem = bound2[j].coordsystem; + bound1[k].side = bound2[j].side; + bound1[k].side2 = bound2[j].side2; + bound1[k].parent = bound2[j].parent; + bound1[k].parent2 = bound2[j].parent2; + bound1[k].material = bound2[j].material; + bound1[k].echain = bound2[j].echain; + bound1[k].types = bound2[j].types; + bound1[k].normal = bound2[j].normal; + + for(i=1; i <= bound1[k].nosides; i++) { + bound1[k].parent[i] += data1->noelements; + if(bound1[k].parent2[i]) + bound1[k].parent2[i] += data1->noelements; + + mat = bound2[j].types[i]; + if( usenames ) { + if( mat < MAXBCS ) { + bound1[k].types[i] = boundarynameis[mat]; + } + } else { + bound1[k].types[i] = bcoffset + mat; + } + } + } + + data1->maxnodes = maxnodes; + newtopo = Imatrix(1,noelements,0,maxnodes-1); + newmaterial = Ivector(1,noelements); + newelementtypes = Ivector(1,noelements); + newx = Rvector(1,noknots); + newy = Rvector(1,noknots); + newz = Rvector(1,noknots); + + for(i=1;i<=data1->noknots;i++) { + newx[i] = data1->x[i]; + newy[i] = data1->y[i]; + newz[i] = data1->z[i]; + } + for(i=1;i<=data2->noknots;i++) { + newx[i+data1->noknots] = data2->x[i]; + newy[i+data1->noknots] = data2->y[i]; + newz[i+data1->noknots] = data2->z[i]; + } for(i=1;i<=data1->noelements;i++) { - newmaterial[i] = data1->material[i]; + mat = data1->material[i]; + newmaterial[i] = mat; newelementtypes[i] = data1->elementtypes[i]; nonodes = newelementtypes[i]%100; for(j=0;jtopology[i][j]; } for(i=1;i<=data2->noelements;i++) { - newmaterial[i+data1->noelements] = data2->material[i]; + mat = data2->material[i]; newelementtypes[i+data1->noelements] = data2->elementtypes[i]; nonodes = newelementtypes[i+data1->noelements]%100; for(j=0;jnoelements][j] = data2->topology[i][j] + data1->noknots; + + if( usenames ) { + if( mat < MAXBODIES ) { + newmaterial[i+data1->noelements] = bodynameis[mat]; + } + } + else { + newmaterial[i+data1->noelements] = bodyoffset + mat; + } } free_Imatrix(data1->topology,1,data1->noelements,0,data1->maxnodes-1); free_Ivector(data1->material,1,data1->noelements); - free_Ivector(data1->elementtypes,1,data1->noelements); free_Rvector(data1->x,1,data1->noknots); free_Rvector(data1->y,1,data1->noknots); free_Rvector(data1->z,1,data1->noknots); - DestroyKnots(data2); + free_Imatrix(data2->topology,1,data2->noelements,0,data2->maxnodes-1); + free_Ivector(data2->material,1,data2->noelements); + free_Rvector(data2->x,1,data2->noknots); + free_Rvector(data2->y,1,data2->noknots); + free_Rvector(data2->z,1,data2->noknots); data1->noelements = noelements; data1->noknots = noknots; @@ -2810,7 +3146,6 @@ int UniteMeshes(struct FemType *data1,struct FemType *data2, data1->y = newy; data1->z = newz; - if(info) printf("Two meshes were united to one with %d nodes and %d elements.\n", noknots,noelements); @@ -2823,22 +3158,32 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, /* Unites two meshes for one larger mesh */ { int i,j,k,l,m; - int noelements,noknots,nonodes,totcopies,ind; - int **newtopo,*newmaterial,*newelementtypes,maxnodes; + int noelements,noknots,nonodes,totcopies,ind,origdim; + int **newtopo=NULL,*newmaterial=NULL,*newelementtypes=NULL,maxnodes; int maxmaterial,maxtype,ncopy,bndr,nosides; - Real *newx,*newy,*newz; + Real *newx=NULL,*newy=NULL,*newz=NULL; Real maxcoord[3],mincoord[3]; - int *vparent,*vparent2,*vside,*vside2,*vtypes,*vmaterial,*vnormal,*vdiscont = NULL; + int *vparent=NULL,*vparent2=NULL,*vside=NULL,*vside2=NULL; + int *vtypes=NULL,*vmaterial=NULL,*vnormal=NULL,*vdiscont=NULL; - printf("CloneMeshes: copying the mesh to a matrix\n"); - if(diffmats) diffmats = 1; - + if(info) printf("CloneMeshes: copying the mesh to a matrix\n"); + if(diffmats) { + if(info) printf("CloneMeshes: giving each new entity new material and bc indexes\n"); + } + + origdim = data->dim; totcopies = 1; + if( ncopies[2] > 1 ) { + data->dim = 3; + } + else { + ncopies[2] = 1; + } + for(i=0;idim;i++) { if(ncopies[i] > 1) totcopies *= ncopies[i]; } - if(data->dim == 2) ncopies[2] = 1; maxcoord[0] = mincoord[0] = data->x[1]; maxcoord[1] = mincoord[1] = data->y[1]; @@ -2853,15 +3198,16 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, if(data->z[i] < mincoord[2]) mincoord[2] = data->z[i]; } - for(i=0;idim;i++) { + for(i=0;i meshsize[i]) meshsize[i] = maxcoord[i]-mincoord[i]; } + if(info) printf("Meshsize to be copied: %lg %lg %lg\n",meshsize[0],meshsize[1],meshsize[2]); noknots = totcopies * data->noknots; noelements = totcopies * data->noelements; maxnodes = data->maxnodes; - printf("Copying the mesh to %d identical domains.\n",totcopies); + if(info) printf("Copying the mesh to %d identical domains in %d-dim.\n",totcopies,data->dim); data->maxnodes = maxnodes; newtopo = Imatrix(1,noelements,0,maxnodes-1); @@ -2871,12 +3217,11 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, newy = Rvector(1,noknots); newz = Rvector(1,noknots); - for(l=0;lnoknots;i++) { - ncopy = j+k*ncopies[0]+k*l*ncopies[1]; + ncopy = j+k*ncopies[0]+l*ncopies[0]*ncopies[1]; ind = i + ncopy*data->noknots; newx[ind] = data->x[i] + j*meshsize[0]; @@ -2888,18 +3233,20 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, } maxmaterial = 0; - for(i=1;i<=data->noelements;i++) - if(data->material[i] > maxmaterial) maxmaterial = data->material[i]; + if( diffmats ) { + for(i=1;i<=data->noelements;i++) + if(data->material[i] > maxmaterial) maxmaterial = data->material[i]; + if(info ) printf("Material offset for cloning set to: %d\n",maxmaterial); + } for(l=0;lnoelements;i++) { - ncopy = j+k*ncopies[0]+k*l*ncopies[1]; + ncopy = j+k*ncopies[0]+l*ncopies[1]*ncopies[0]; ind = i + ncopy*data->noelements; newmaterial[ind] = data->material[i] + diffmats*maxmaterial*ncopy; - newelementtypes[ind] = data->elementtypes[i]; nonodes = newelementtypes[i]%100; for(m=0;mnoelements; @@ -2951,8 +3302,8 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, vside2[ind] = bound[bndr].side2[i]; } else { - vparent2[ind] = 0; - vside2[ind] = 0; + vparent2[ind] = 0.0; + vside2[ind] = 0.0; } vnormal[ind] = bound[bndr].normal[i]; @@ -2967,7 +3318,7 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, } } } - + bound[bndr].nosides = nosides; bound[bndr].side = vside; @@ -2990,11 +3341,18 @@ int CloneMeshes(struct FemType *data,struct BoundaryType *bound, data->noknots = noknots; data->topology = newtopo; data->material = newmaterial; + data->elementtypes = newelementtypes; data->x = newx; data->y = newy; data->z = newz; + if( data->bodynamesexist || data->boundarynamesexist ) { + printf("Cloning cannot treat names yet, omitting treatment of names for now!\n"); + data->bodynamesexist = FALSE; + data->boundarynamesexist = FALSE; + } + if(info) printf("The mesh was copied to several identical meshes\n"); return(0); @@ -3006,14 +3364,15 @@ int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, /* Makes a mirror image of a mesh and unites it with the original mesh */ { int i,j,m; - int noelements,noknots,nonodes,totcopies,ind; - int **newtopo,*newmaterial,*newelementtypes,maxnodes; + int noelements,noknots,nonodes,totcopies,ind,maxnodes; + int **newtopo=NULL,*newmaterial=NULL,*newelementtypes=NULL; int maxtype,bndr,nosides; - Real *newx,*newy,*newz; + Real *newx=NULL,*newy=NULL,*newz=NULL; Real maxcoord[3],mincoord[3]; int ind0,elem0,axis1,axis2,axis3,symmcount; - int *vparent,*vparent2,*vside,*vside2,*vtypes,*vmaterial,*vnormal,*vdiscont = NULL; + int *vparent=NULL,*vparent2=NULL,*vside=NULL,*vside2=NULL; + int *vtypes=NULL,*vmaterial=NULL,*vnormal=NULL,*vdiscont=NULL; printf("MirrorMeshes: making a symmetric mapping of the mesh\n"); @@ -3035,14 +3394,14 @@ int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, if(data->z[i] < mincoord[2]) mincoord[2] = data->z[i]; } - for(i=0;idim;i++) { + for(i=0;i<3;i++) { if(maxcoord[i]-mincoord[i] > meshsize[i]) meshsize[i] = maxcoord[i]-mincoord[i]; } if(diffmats) diffmats = 1; totcopies = 1; - for(i=0;idim;i++) + for(i=0;i<3;i++) if(symmaxis[i]) totcopies *= 2; noknots = totcopies * data->noknots; @@ -3071,7 +3430,7 @@ int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, newx[ind] = (1-2*axis1) * data->x[i]; newy[ind] = (1-2*axis2) * data->y[i]; - newz[ind] = (1-2*axis3)*data->z[i]; + newz[ind] = (1-2*axis3) * data->z[i]; newmaterial[ind] = data->material[i]; newelementtypes[ind] = data->elementtypes[i]; @@ -3192,6 +3551,12 @@ int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, data->y = newy; data->z = newz; + if( data->bodynamesexist || data->boundarynamesexist ) { + printf("Mirroring cannot treat names yet, omitting treatment of names for now!\n"); + data->bodynamesexist = FALSE; + data->boundarynamesexist = FALSE; + } + if(info) printf("The mesh was copied to several identical meshes\n"); return(0); @@ -3203,10 +3568,10 @@ static void ReorderAutomatic(struct FemType *data,int iterations, int *origindx,Real corder[],int info) { int i,j,k,l,nonodes,maxnodes,noelements,noknots,minwidth,indexwidth; - int **neighbours,*newrank,*newindx,*oldrank,*oldindx; - int nocands = 0,*cands,ind,ind2,cantdo; - int elemtype,indready,iter,*localorder,*localtmp,nolocal; - Real *localdist,dx,dy,dz; + int **neighbours=NULL,*newrank=NULL,*newindx=NULL,*oldrank=NULL,*oldindx=NULL; + int nocands,*cands=NULL,ind,ind2,cantdo; + int elemtype,indready,iter,*localorder=NULL,*localtmp=NULL,nolocal; + Real *localdist=NULL,dx,dy,dz; iterations = 3; iter = 0; @@ -3257,6 +3622,7 @@ static void ReorderAutomatic(struct FemType *data,int iterations, for(j=1;j<=noelements;j++) { elemtype = data->elementtypes[j]; nonodes = elemtype%100; + nocands = 0; for(i=0;itopology[j][i]; @@ -3313,7 +3679,7 @@ static void ReorderAutomatic(struct FemType *data,int iterations, localtmp[l] = ind; dx = data->x[l] - data->x[ind]; dy = data->y[l] - data->y[ind]; - if(data->dim == 3) dz = data->z[l] - data->z[ind]; + dz = data->z[l] - data->z[ind]; localdist[l] = corder[0]*fabs(dx) + corder[1]*fabs(dy) + corder[2]*fabs(dz); } } @@ -3403,10 +3769,10 @@ void ReorderElements(struct FemType *data,struct BoundaryType *bound, { int i,j,k; int noelements,noknots,nonodes,length; - int **newtopology,*newmaterial,*newelementtypes; - int *indx,*revindx,*elemindx,*revelemindx; + int **newtopology=NULL,*newmaterial=NULL,*newelementtypes=NULL; + int *indx=NULL,*revindx=NULL,*elemindx=NULL,*revelemindx=NULL; int oldnoknots, oldnoelements; - Real *newx,*newy,*newz,*arrange; + Real *newx=NULL,*newy=NULL,*newz=NULL,*arrange=NULL; Real dx,dy,dz,cx,cy,cz,cbase; noelements = oldnoelements = data->noelements; @@ -3432,35 +3798,29 @@ void ReorderElements(struct FemType *data,struct BoundaryType *bound, cz = corder[2]; } else { - Real xmin,xmax,ymin,ymax,zmin = 0,zmax = 0; + Real xmin,xmax,ymin,ymax,zmin,zmax; xmin = xmax = data->x[1]; ymin = ymax = data->y[1]; - if(data->dim == 3) zmin = zmax = data->z[1]; + zmin = zmax = data->z[1]; + for(i=1;i<=data->noknots;i++) { if(xmin > data->x[i]) xmin = data->x[i]; if(xmax < data->x[i]) xmax = data->x[i]; if(ymin > data->y[i]) ymin = data->y[i]; if(ymax < data->y[i]) ymax = data->y[i]; - if(data->dim == 3) { - if(zmin > data->z[i]) zmin = data->z[i]; - if(zmax < data->z[i]) zmax = data->z[i]; - } + if(zmin > data->z[i]) zmin = data->z[i]; + if(zmax < data->z[i]) zmax = data->z[i]; } dx = xmax-xmin; dy = ymax-ymin; - if(data->dim == 3) dz = zmax-zmin; - else dz = 0.0; + dz = zmax-zmin; + /* The second strategy seems to be better in many cases */ -#if 0 - cx = dx; - cy = dy; - cz = dz; -#else cbase = 100.0; cx = pow(cbase,1.0*(dx>dy)+1.0*(dx>dz)); cy = pow(cbase,1.0*(dy>dx)+1.0*(dy>dz)); cz = pow(cbase,1.0*(dz>dx)+1.0*(dz>dx)); -#endif + corder[0] = cx; corder[1] = cy; corder[2] = cz; @@ -3468,8 +3828,7 @@ void ReorderElements(struct FemType *data,struct BoundaryType *bound, if(info) printf("Ordering with (%.3lg*x + %.3lg*y + %.3lg*z)\n",cx,cy,cz); for(i=1;i<=noknots;i++) { - arrange[i] = cx*data->x[i] + cy*data->y[i]; - if(data->dim == 3) arrange[i] += cz*data->z[i]; + arrange[i] = cx*data->x[i] + cy*data->y[i] + cz*data->z[i]; } SortIndex(noknots,arrange,indx); @@ -3484,8 +3843,7 @@ void ReorderElements(struct FemType *data,struct BoundaryType *bound, arrange[j] = 0.0; for(i=0;itopology[j][i]; - arrange[j] += cx*data->x[k] + cy*data->y[k]; - if(data->dim == 3) arrange[j] += cz*data->z[k]; + arrange[j] += cx*data->x[k] + cy*data->y[k] + cz*data->z[k]; } } @@ -3621,13 +3979,15 @@ int RemoveUnusedNodes(struct FemType *data,int info) + void RenumberBoundaryTypes(struct FemType *data,struct BoundaryType *bound, int renumber, int bcoffset, int info) { - int i,j,k,l,doinit; - int minbc=0,maxbc=0,*mapbc; - int elemdim=0,elemtype=0,*mapdim,sideind[MAXNODESD1]; - + int i,j,k,doinit,isordered; + int minbc=0,maxbc=0,**mapbc; + int elemdim=0,elemtype=0,sideind[MAXNODESD1]; + int bctype; + if(renumber) { if(0) printf("Renumbering boundary types\n"); @@ -3646,49 +4006,87 @@ void RenumberBoundaryTypes(struct FemType *data,struct BoundaryType *bound, } if(doinit) return; - mapbc = Ivector(minbc,maxbc); - mapdim = Ivector(minbc,maxbc); - for(i=minbc;i<=maxbc;i++) mapbc[i] = mapdim[i] = 0; - + if(info) printf("Initial boundary interval [%d,%d]\n",minbc,maxbc); + + mapbc = Imatrix(minbc,maxbc,0,2); + for(i=minbc;i<=maxbc;i++) + for(j=0;j<=2;j++) + mapbc[i][j] = 0; + for(j=0;j < MAXBOUNDARIES;j++) { if(!bound[j].created) continue; for(i=1;i<=bound[j].nosides;i++) { GetElementSide(bound[j].parent[i],bound[j].side[i],bound[j].normal[i],data,sideind,&elemtype); + if(!elemtype) printf("could not find boundary element: %d %d %d\n",i,j,bound[j].parent[i]); elemdim = GetElementDimension(elemtype); - mapbc[bound[j].types[i]] = TRUE; - mapdim[bound[j].types[i]] = elemdim; + bctype = bound[j].types[i]; + + if(0) printf("type and dim: %d %d %d\n",elemtype,elemdim,bctype); + + mapbc[bctype][elemdim] += 1; } } - + + if(0) { + for(i=minbc;i<=maxbc;i++) + for(j=0;j<=2;j++) + if(mapbc[i][j]) printf("bc map1: %d %d\n",i,mapbc[i][j]); + } + j = 0; /* Give the larger dimension always a smaller BC type */ + isordered = TRUE; for(elemdim=2;elemdim>=0;elemdim--) { for(i=minbc;i<=maxbc;i++) { - if(mapdim[i] != elemdim) continue; - if(mapbc[i]) { - j++; - mapbc[i] = j; - } + if(!mapbc[i][elemdim]) continue; + j++; + if(i == j) { + printf("boundary index unaltered %d in %d %dD elements\n",i,mapbc[i][elemdim],elemdim); + } + else { + isordered = FALSE; + printf("boundary index changed %d -> %d in %d %dD elements\n",i,j,mapbc[i][elemdim],elemdim); + } + mapbc[i][elemdim] = j; } } - if(maxbc - minbc >= j || minbc != 1) { + if(0) { + for(i=minbc;i<=maxbc;i++) + for(j=0;j<=2;j++) + if(mapbc[i][j]) printf("bc map2: %d %d\n",i,mapbc[i][j]); + } + + if(isordered) { + if(info) printf("Numbering of boundary types is already ok\n"); + } + else { if(info) printf("Mapping boundary types from [%d %d] to [%d %d]\n",minbc,maxbc,1,j); for(j=0;j < MAXBOUNDARIES;j++) { if(!bound[j].created) continue; for(i=1;i<=bound[j].nosides;i++) { - bound[j].types[i] = mapbc[bound[j].types[i]]; + GetElementSide(bound[j].parent[i],bound[j].side[i],bound[j].normal[i],data,sideind,&elemtype); + elemdim = GetElementDimension(elemtype); + bound[j].types[i] = mapbc[bound[j].types[i]][elemdim]; } } if(data->boundarynamesexist) { + char boundaryname0[MAXBCS][MAXNAMESIZE]; + + /* We need some temporal place is name mapping might not be unique */ + for(j=minbc;j<=MIN(maxbc,MAXBODIES-1);j++) + strcpy(boundaryname0[j],data->boundaryname[j]); + for(j=minbc;j<=MIN(maxbc,MAXBODIES-1);j++) { - if(mapbc[j]) - strcpy(data->boundaryname[mapbc[j]],data->boundaryname[j]); + for(elemdim=2;elemdim>=0;elemdim--) { + k = mapbc[j][elemdim]; + if(k) strcpy(data->boundaryname[k],boundaryname0[j]); + } } } } - free_Ivector(mapbc,minbc,maxbc); + free_Imatrix(mapbc,minbc,maxbc,0,2); } if(bcoffset) { @@ -3704,13 +4102,13 @@ void RenumberBoundaryTypes(struct FemType *data,struct BoundaryType *bound, } } } -} +} void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int info) { - int i,j,k,l,noelements,doinit; + int i,j,noelements,doinit; int minmat=0,maxmat=0,*mapmat; if(0) printf("Setting new material types\n"); @@ -3731,16 +4129,23 @@ void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int i minmat = MIN(minmat,data->material[j]); } + if(info) printf("Initial body interval [%d,%d]\n",minmat,maxmat); + mapmat = Ivector(minmat,maxmat); for(i=minmat;i<=maxmat;i++) mapmat[i] = 0; for(j=1;j<=noelements;j++) - mapmat[data->material[j]] = TRUE; + mapmat[data->material[j]] += 1; j = 0; - for(i=minmat;i<=maxmat;i++) - if(mapmat[i]) mapmat[i] = ++j; - + for(i=minmat;i<=maxmat;i++) { + if(mapmat[i]) { + j++; + if(i != j) printf("body index changed %d -> %d in %d elements\n",i,j,mapmat[i]); + mapmat[i] = j; + } + } + if(maxmat - minmat >= j || minmat != 1) { if(info) printf("Mapping material types from [%d %d] to [%d %d]\n", minmat,maxmat,1,j); @@ -3755,7 +4160,7 @@ void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int i } } else { - if(info) printf("Materials ordered continuously between %d and %d\n",minmat,maxmat); + if(info) printf("Numbering of bodies is already ok\n"); } free_Ivector(mapmat,minmat,maxmat); } @@ -3765,7 +4170,7 @@ void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int i int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *bound,int info) { int i,j,noelements; - int maxelemtype,maxelemdim,elemdim; + int elemtype,maxelemdim,minelemdim,elemdim; int parent, side, sideind[MAXNODESD1],sideelemtype; int nosides, oldnosides,newnosides; @@ -3774,11 +4179,16 @@ int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *b noelements = data->noelements; if(noelements < 1) return(1); - maxelemtype = GetMaxElementType(data); - maxelemdim = GetElementDimension(maxelemtype); + elemtype = GetMaxElementType(data); + maxelemdim = GetElementDimension(elemtype); + if(info) printf("Maximum elementtype is %d and dimension %d\n",elemtype,maxelemdim); + + elemtype = GetMinElementType(data); + minelemdim = GetElementDimension(elemtype); + if(info) printf("Minimum elementtype is %d and dimension %d\n",elemtype,minelemdim); - if(info) printf("Maximum elementtype is %d and dimension %d\n",maxelemtype,maxelemdim); - if(maxelemdim < 2) return(2); + /* Nothing to remove if the bulk mesh has 1D elements */ + if(minelemdim < 2) return(2); oldnosides = 0; newnosides = 0; @@ -3792,14 +4202,11 @@ int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *b side = bound[j].side[i]; GetElementSide(parent,side,1,data,sideind,&sideelemtype); - if(sideelemtype > 300) - elemdim = 2; - else if(sideelemtype > 200) - elemdim = 1; - else - elemdim = 0; + elemdim = GetElementDimension(sideelemtype); - if(maxelemdim - elemdim > 1) continue; + /* if(maxelemdim - elemdim > 1) continue; */ + /* This was changed as we want to maintain 1D BCs of a hybrid 2D/3D mesh. */ + if(minelemdim - elemdim > 1) continue; nosides++; if(nosides == i) continue; @@ -3815,17 +4222,57 @@ int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *b } if(info) printf("Removed %d (out of %d) less than %dD boundary elements\n", - oldnosides-newnosides,oldnosides,maxelemdim); + oldnosides-newnosides,oldnosides,minelemdim-1); return(0); } +int RemoveInternalBoundaries(struct FemType *data,struct BoundaryType *bound,int info) +{ + int i,j; + int parent,parent2; + int nosides,oldnosides,newnosides; + + if(info) printf("Removing internal boundaries\n"); + + if( data->noelements < 1 ) return(1); + + oldnosides = 0; + newnosides = 0; + for(j=0;j < MAXBOUNDARIES;j++) { + nosides = 0; + if(!bound[j].created) continue; + for(i=1;i<=bound[j].nosides;i++) { + + oldnosides++; + parent = bound[j].parent[i]; + parent2 = bound[j].parent2[i]; + + if( parent > 0 && parent2 > 0 ) continue; + + nosides++; + if(nosides == i) continue; + + bound[j].parent[nosides] = bound[j].parent[i]; + bound[j].parent2[nosides] = bound[j].parent2[i]; + bound[j].side[nosides] = bound[j].side[i]; + bound[j].side2[nosides] = bound[j].side2[i]; + bound[j].types[nosides] = bound[j].types[i]; + } + bound[j].nosides = nosides; + newnosides += nosides; + } + if(info) printf("Removed %d (out of %d) internal boundary elements\n", + oldnosides-newnosides,oldnosides); + return(0); +} +#if 0 static void FindEdges(struct FemType *data,struct BoundaryType *bound, int material,int sidetype,int info) { - int i,j,side,identical,element; + int i,j,side,identical,noelements,element; int noknots,nomaterials,nosides,newbound; int maxelementtype,maxedgenodes,elemedges,maxelemedges,edge,dosides; int **edgetable,sideind[MAXNODESD1],sideelemtype,allocated; @@ -3833,8 +4280,10 @@ static void FindEdges(struct FemType *data,struct BoundaryType *bound, Real *arrange; + newbound = 0; nomaterials = 0; maxelementtype = 0; + noelements = data->noelements; printf("FindEdges: Finding edges of bulk elements of type %d\n",material); maxelementtype = GetMaxElementType(data); @@ -3855,7 +4304,7 @@ static void FindEdges(struct FemType *data,struct BoundaryType *bound, edgetable[i][j] = 0; edge = 0; - for(element=1;element<=data->noelements;element++) { + for(element=1;element<=noelements;element++) { if(data->material[element] != material) continue; elemedges = data->elementtypes[element]/100; @@ -3892,18 +4341,6 @@ static void FindEdges(struct FemType *data,struct BoundaryType *bound, SortIndex(noknots,arrange,indx); -#if 0 - printf("noknots = %d\n",noknots); - for(i=1;i<=noknots;i++) - printf("indx[%d]=%d edge=%d arrange[%d] = %lg arrange[indx[%d]] = %lg\n", - i,indx[i],edgetable[i][0],i,arrange[i],i,arrange[indx[i]]); -#endif -#if 0 - revindx = Ivector(1,data->noknots); - for(i=1;i<=noknots;i++) - revindx[indx[i]] = i; -#endif - allocated = FALSE; omstart: @@ -3947,11 +4384,10 @@ static void FindEdges(struct FemType *data,struct BoundaryType *bound, goto omstart; } - free_Ivector(indx,1,noknots); free_Imatrix(edgetable,1,maxelemedges*nomaterials,0,maxedgenodes+1); } - +#endif static int CompareIndexes(int elemtype,int *ind1,int *ind2) { @@ -3972,11 +4408,12 @@ static int CompareIndexes(int elemtype,int *ind1,int *ind2) int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, int *boundnodes,int suggesttype,int dimred,int info) { - int i,j,side,identical,element,lowerdim,dim,minedge = 0,maxedge = 0; - int nonodes,nosides,newbound = 0; + int i,j,side,identical,element,lowerdim,dim,minedge,maxedge; + int noelements,noknots,nonodes,nosides,newbound; int sideind[MAXNODESD1],sideind0[MAXNODESD1],sideelemtype,sideelemtype0,allocated; - int noboundnodes,sameside,newtype = 0,elemtype; + int noboundnodes,sameside,newtype,elemtype; + newtype = 0; allocated = FALSE; dim = data->dim; if(dimred) @@ -3984,8 +4421,14 @@ int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, else lowerdim = dim-1; + noknots = data->noknots; + noelements = data->noelements; noboundnodes = 0; - for(i=1;i<=data->noknots;i++) + newbound = 0; + maxedge = 0; + minedge = 0; + + for(i=1;i<=noknots;i++) if(boundnodes[i]) noboundnodes++; if(!noboundnodes) { printf("FindNewBoundaries: no nonzero entries in boundnodes vector!\n"); @@ -3998,7 +4441,7 @@ int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, omstart: nosides = 0; - for(element=1;element<=data->noelements;element++) { + for(element=1;element<=noelements;element++) { elemtype = data->elementtypes[element]; if(dim == 1) { @@ -4134,9 +4577,9 @@ int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, int FindBulkBoundary(struct FemType *data,int mat1,int mat2, int *boundnodes,int *noboundnodes,int info) { - int i,j,k = 0; + int i,j,k; int nonodes,maxnodes,minnodes,material; - Real ds,xmin = 0,xmax = 0,ymin = 0,ymax = 0,zmin = 0,zmax = 0,eps; + Real ds,xmin=0.0,xmax=0.0,ymin=0.0,ymax=0.0,zmin=0.0,zmax=0.0,eps; int *visited,elemdim,*ind; Real *anglesum,dx1,dx2,dy1,dy2,dz1,dz2,ds1,ds2,dotprod; @@ -4214,7 +4657,7 @@ int FindBulkBoundary(struct FemType *data,int mat1,int mat2, for(i=1;i<=data->noknots;i++) { anglesum[i] /= 2.0 * FM_PI; if(anglesum[i] > 0.99) visited[i] = 0; - if(anglesum[i] > 1.01) printf("FindBulkBoundary: surpricingly large angle %.3le in node %d\n",anglesum[i],i); + if(anglesum[i] > 1.01) printf("FindBulkBoundary: surpricingly large angle %.3e in node %d\n",anglesum[i],i); if(visited[i]) j++; } if(0) printf("There are %d boundary node candidates\n",j); @@ -4279,28 +4722,23 @@ int FindBulkBoundary(struct FemType *data,int mat1,int mat2, for(i=1;i<=data->noknots;i++) if(visited[i]) { if(j) { - xmax = xmin = data->x[k]; - if(data->dim >= 2) ymax = ymin = data->y[k]; - if(data->dim >= 3) zmax = zmin = data->z[k]; + xmax = xmin = data->x[i]; + ymax = ymin = data->y[i]; + zmax = zmin = data->z[i]; j = FALSE; } else { if(data->x[i] > xmax) xmax = data->x[i]; if(data->x[i] < xmin) xmin = data->x[i]; - if(data->dim >= 2) { - if(data->y[i] > ymax) ymax = data->y[i]; - if(data->y[i] < ymin) ymin = data->y[i]; - } - if(data->dim >= 3) { - if(data->z[i] > zmax) zmax = data->z[i]; - if(data->z[i] < zmin) zmin = data->z[i]; - } + if(data->y[i] > ymax) ymax = data->y[i]; + if(data->y[i] < ymin) ymin = data->y[i]; + if(data->z[i] > zmax) zmax = data->z[i]; + if(data->z[i] < zmin) zmin = data->z[i]; } } - ds = (xmax-xmin)*(xmax-xmin); - if(data->dim >= 2) ds += (ymax-ymin)*(ymax-ymin); - if(data->dim >= 3) ds += (zmax-zmin)*(zmax-zmin); + ds = (xmax-xmin)*(xmax-xmin) + + (ymax-ymin)*(ymax-ymin) + (zmax-zmin)*(zmax-zmin); ds = sqrt(ds); eps = 1.0e-5 * ds; @@ -4350,8 +4788,9 @@ int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat { int i,j,k,l; int hits,nonodes,nocorners,maxnodes,minnodes,elemtype,material,bounddim; - Real ds,xmin,xmax,ymin = 0,ymax = 0,zmin = 0,zmax = 0,eps,dx1,dx2,dy1,dy2,dz1,dz2,ds1,ds2,dotprod; - Real *anglesum = NULL; + Real ds,xmin=0.0,xmax=0.0,ymin=0.0,ymax=0.0,zmin=0.0,zmax=0.0; + Real eps,dx1,dx2,dy1,dy2,dz1,dz2,ds1,ds2,dotprod; + Real *anglesum=NULL; int *visited,sideind[MAXNODESD2],elemind[MAXNODESD2]; eps = 1.0e-4; @@ -4437,7 +4876,7 @@ int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat for(i=1;i<=data->noknots;i++) { anglesum[i] /= 2.0 * FM_PI; if(anglesum[i] > 0.99) visited[i] = 0; - if(anglesum[i] > 1.01) printf("FindBulkBoundary: surpricingly large angle %.3le in node %d\n",anglesum[i],i); + if(anglesum[i] > 1.01) printf("FindBulkBoundary: surpricingly large angle %.3e in node %d\n",anglesum[i],i); } free_Rvector(anglesum,1,data->noknots); @@ -4511,26 +4950,21 @@ int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat l = sideind[0]; xmax = xmin = data->x[l]; - if(data->dim >= 2) ymax = ymin = data->y[l]; - if(data->dim >= 3) zmax = zmin = data->z[l]; + ymax = ymin = data->y[l]; + zmax = zmin = data->z[l]; for(k=1;kx[l] > xmax) xmax = data->x[l]; if(data->x[l] < xmin) xmin = data->x[l]; - if(data->dim >= 2) { - if(data->y[l] > ymax) ymax = data->y[l]; - if(data->y[l] < ymin) ymin = data->y[l]; - } - if(data->dim >= 3) { - if(data->z[l] > zmax) zmax = data->z[l]; - if(data->z[l] < zmin) zmin = data->z[l]; - } + if(data->y[l] > ymax) ymax = data->y[l]; + if(data->y[l] < ymin) ymin = data->y[l]; + if(data->z[l] > zmax) zmax = data->z[l]; + if(data->z[l] < zmin) zmin = data->z[l]; } - ds = (xmax-xmin)*(xmax-xmin); - if(data->dim >= 2) ds += (ymax-ymin)*(ymax-ymin); - if(data->dim >= 3) ds += (zmax-zmin)*(zmax-zmin); + ds = (xmax-xmin)*(xmax-xmin) + + (ymax-ymin)*(ymax-ymin) + (zmax-zmin)*(zmax-zmin); ds = sqrt(ds); eps = 1.0e-3 * ds; @@ -4570,8 +5004,7 @@ int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat printf("FindBoundaryBoundary: unknown option %d for finding a side\n",mat2); return(2); } - - + *noboundnodes = 0; for(i=1;i<=data->noknots;i++) if(boundnodes[i]) *noboundnodes += 1; @@ -4588,17 +5021,19 @@ int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat int IncreaseElementOrder(struct FemType *data,int info) { int i,j,side,element,maxcon,con,newknots,ind,ind2; - int noelements,noknots,nonodes,maxnodes = 0,maxelemtype,hit,node,elemtype; - int **newnodetable,inds[2],**newtopo; - Real *newx,*newy,*newz; + int noelements,noknots,nonodes,maxnodes,maxelemtype,hit,node; + int elemtype; + int **newnodetable=NULL,inds[2],**newtopo=NULL; + Real *newx=NULL,*newy=NULL,*newz=NULL; if(info) printf("Trying to increase the element order of current elements\n"); - CreateDualGraph(data,FALSE,info); + CreateNodalGraph(data,FALSE,info); noknots = data->noknots; noelements = data->noelements; - maxcon = data->dualmaxconnections; + maxcon = data->nodalmaxconnections; + maxnodes = 0; newnodetable = Imatrix(0,maxcon-1,1,noknots); for(i=1;i<=noknots;i++) @@ -4608,7 +5043,7 @@ int IncreaseElementOrder(struct FemType *data,int info) newknots = 0; for(i=1;i<=noknots;i++) { for(j=0;jdualgraph[j][i]; + con = data->nodalgraph[j][i]; if(con > i) { newknots++; newnodetable[j][i] = noknots + newknots; @@ -4630,7 +5065,7 @@ int IncreaseElementOrder(struct FemType *data,int info) } for(i=1;i<=noknots;i++) { for(j=0;jdualgraph[j][i]; + con = data->nodalgraph[j][i]; ind = newnodetable[j][i]; if(con && ind) { newx[ind] = 0.5*(data->x[i] + data->x[con]); @@ -4688,7 +5123,7 @@ int IncreaseElementOrder(struct FemType *data,int info) ind2 = inds[1]; } for(j=0;jdualgraph[j][ind]; + con = data->nodalgraph[j][ind]; if(con == ind2) { node = newnodetable[j][ind]; @@ -4701,7 +5136,7 @@ int IncreaseElementOrder(struct FemType *data,int info) data->elementtypes[element] = elemtype; } - DestroyDualGraph(data,info); + DestroyNodalGraph(data,info); free_Rvector(data->x,1,data->noknots); free_Rvector(data->y,1,data->noknots); @@ -4724,38 +5159,226 @@ int IncreaseElementOrder(struct FemType *data,int info) - - -static void CylindricalCoordinateTransformation(struct FemType *data,Real r1,Real r2, - int rectangle) +int IncreaseElementOrderOld(struct FemType *data,int info) { - int i,j,j2,ind1,ind2,nonodes1; - Real x,y,r,f,z,q,x2,y2,z2,dx,dy,dz,eps,mult; - int hits,trials,tests; - int candidates,*candidatelist,*indx; - - if(rectangle) { - printf("Rectangular geometry with r1=%.4lg for %d nodes.\n", - r1,data->noknots); - } - else { - printf("Cylindrical geometry with r1=%.4lg r2=%.4lg for %d nodes.\n", - r1,r2,data->noknots); - } + int i,j,side,element,noedges,elemtype,newnode; + int noelements,noknots,nosides,maxnodes; + int maxelementtype,maxedgenodes,elemedges,maxelemedges,edge,dosides; + int **edgetable=NULL,sideind[MAXNODESD1],sideelemtype,allocated; + int *indx=NULL,*identical=NULL,**newtopo=NULL; + Real *arrange=NULL,*newx=NULL,*newy=NULL,*newz=NULL; + if(info) printf("Trying to increase the element order of current elements\n"); + + maxelementtype = 0; + maxnodes = 0; + noedges = 0; - for(i=1;i<=data->noknots;i++) { - r = data->x[i]; - z = data->y[i]; - f = data->z[i]; + noelements = data->noelements; + noknots = data->noknots; - data->z[i] = z; + maxelementtype = GetMaxElementType(data); - if(r >= r2) { - data->x[i] = cos(f)*r; - data->y[i] = sin(f)*r; - } - else if(r <= r2) { + if(maxelementtype/100 > 4) { + printf("IncreaseElementOrder: Implemented only for 2D elements!\n"); + dosides = 0; + return(1); + } + + if(maxelementtype/100 <= 2) maxedgenodes = 1; + else if(maxelementtype/100 <= 4) maxedgenodes = 2; + maxelemedges = maxelementtype/100; + allocated = FALSE; + + edgeloop: + + edge = 0; + for(element=1;element<=data->noelements;element++) { + + elemedges = data->elementtypes[element]/100; + + for(side=0;side sideind[1]) { + edgetable[edge][0] = sideind[0]; + edgetable[edge][1] = sideind[1]; + } + else { + edgetable[edge][1] = sideind[0]; + edgetable[edge][0] = sideind[1]; + } + } + } + } + + if(!allocated) { + noedges = edge; + edgetable = Imatrix(1,noedges,0,maxedgenodes+1); + for(i=1;i<=noedges;i++) + for(j=0;j<=maxedgenodes+1;j++) + edgetable[i][j] = 0; + allocated = TRUE; + goto edgeloop; + } + + printf("There are altogether %d edges in the elements\n",noedges); + + arrange = Rvector(1,noedges); + for(i=1;i<=noedges;i++) + arrange[i] = 0.0; + for(i=1;i<=noedges;i++) + arrange[i] = edgetable[i][0]; + indx = Ivector(1,noedges); + + SortIndex(noedges,arrange,indx); + +#if 0 + printf("noknots = %d\n",noknots); + for(i=1;i<=noknots;i++) + printf("indx[%d]=%d edge=%d arrange[%d] = %g arrange[indx[%d]] = %g\n", + i,indx[i],edgetable[i][0],i,arrange[i],i,arrange[indx[i]]); +#endif +#if 0 + revindx = Ivector(1,data->noknots); + for(i=1;i<=noknots;i++) + revindx[indx[i]] = i; +#endif + + allocated = FALSE; + identical = Ivector(1,noedges); + for(i=1;i<=noedges;i++) identical[i] = 0; + + nosides = 0; + for(i=1;i<=noedges;i++) { + if(identical[i] < 0) continue; + if(maxedgenodes == 1) { + for(j=i+1;j<=noedges && edgetable[indx[i]][0] == edgetable[indx[j]][0];j++) + identical[j] = -i; + } + else if(maxedgenodes == 2) { + for(j=i+1;j<=noedges && edgetable[indx[i]][0] == edgetable[indx[j]][0];j++) + if(edgetable[indx[i]][1] == edgetable[indx[j]][1]) + identical[j] = -i; + } + identical[i] = ++nosides; + } + + printf("There will be %d new nodes in the elements\n",nosides); + + newx = Rvector(1,noknots+nosides); + newy = Rvector(1,noknots+nosides); + newz = Rvector(1,noknots+nosides); + + for(i=1;i<=noknots;i++) { + newx[i] = data->x[i]; + newy[i] = data->y[i]; + newz[i] = data->z[i]; + } + + if(maxelementtype <= 303) + maxnodes = 6; + else if(maxelementtype == 404) + maxnodes = 8; + newtopo = Imatrix(1,noelements,0,maxnodes-1); + + for(element=1;element<=noelements;element++) { + elemtype = data->elementtypes[element]; + elemedges = elemtype/100; + for(i=0;itopology[element][i]; + } + + + for(j=1;j<=noedges;j++) { + newnode = identical[j]; + if(newnode < 0) newnode = identical[abs(newnode)]; + if(newnode <= 0) printf("Newnode = %d Edge = %d\n",newnode,j); + newnode += noknots; + + edge = indx[j]; + element = edgetable[edge][maxedgenodes]; + side = edgetable[edge][maxedgenodes+1]; + + GetElementSide(element,side,1,data,sideind,&sideelemtype); + + elemtype = data->elementtypes[element]; + + newtopo[element][elemtype/100+side] = newnode; + if(elemtype == 303) + data->elementtypes[element] = 306; + else if(elemtype == 404) + data->elementtypes[element] = 408; + + newx[newnode] = 0.5*(data->x[sideind[0]] + data->x[sideind[1]]); + newy[newnode] = 0.5*(data->y[sideind[0]] + data->y[sideind[1]]); + newz[newnode] = 0.5*(data->z[sideind[0]] + data->z[sideind[1]]); + } + + free_Rvector(data->x,1,data->noknots); + free_Rvector(data->y,1,data->noknots); + free_Rvector(data->z,1,data->noknots); + free_Imatrix(data->topology,1,data->noelements,0,data->maxnodes); + + + data->x = newx; + data->y = newy; + data->z = newz; + + data->topology = newtopo; + data->noknots += nosides; + data->maxnodes = maxnodes; + + free_Ivector(indx,1,noedges); + free_Ivector(identical,1,noedges); + free_Imatrix(edgetable,1,noedges,0,maxedgenodes+1); + + printf("Created extra nodes in the middle of the edges\n"); + + return(0); +} + + + + +static void CylindricalCoordinateTransformation(struct FemType *data,Real r1,Real r2, + int rectangle) +{ + int i,j,j2,ind1,ind2,nonodes1; + Real x,y,r,f,z,q,x2,y2,z2,dx,dy,dz,eps,mult; + int hits,trials,tests; + int candidates,*candidatelist=NULL,*indx=NULL; + + if(rectangle) { + printf("Rectangular geometry with r1=%.4lg for %d nodes.\n", + r1,data->noknots); + } + else { + printf("Cylindrical geometry with r1=%.4lg r2=%.4lg for %d nodes.\n", + r1,r2,data->noknots); + } + + + for(i=1;i<=data->noknots;i++) { + r = data->x[i]; + z = data->y[i]; + f = data->z[i]; + + data->z[i] = z; + + if(r >= r2) { + data->x[i] = cos(f)*r; + data->y[i] = sin(f)*r; + } + else if(r <= r2) { mult = r/r1; @@ -4938,12 +5561,12 @@ static void CylindricalCoordinateImprove(struct FemType *data,Real factor, } -static void CylindricalCoordinateCurve(struct FemType *data, - Real zet,Real rad,Real angle) +void CylindricalCoordinateCurve(struct FemType *data, + Real zet,Real rad,Real angle) { int i; Real x,y,z; - Real z0,z1,f0,f,z2,x2,r0; + Real z0,z1,f,f0,z2,x2,r0; printf("Cylindrical coordinate curve, zet=%.3lg rad=%.3lg angle=%.3lg\n", zet,rad,angle); @@ -4954,26 +5577,38 @@ static void CylindricalCoordinateCurve(struct FemType *data, z1 = z0+r0*f0; for(i=1;i<=data->noknots;i++) { - x = data->x[i]; - y = data->y[i]; - z = data->z[i]; - - if(z <= z0) continue; + if(data->dim == 2) { + z = data->x[i]; + x = data->y[i]; + } + else { + x = data->x[i]; + y = data->y[i]; + z = data->z[i]; + } + + if(z <= z0) continue; + if(z >= z1) { z2 = z0 + sin(f0)*(r0+x) + cos(f0)*(z-z1); x2 = (cos(f0)-1.0)*r0 + cos(f0)*x - sin(f0)*(z-z1); - data->z[i] = z2; - data->x[i] = x2; } else { f = (z-z0)/r0; z2 = z0 + sin(f)*(r0+x); x2 = (cos(f)-1.0)*r0 + cos(f)*x; + } + + if( data->dim == 2) { + data->x[i] = z2; + data->y[i] = x2; + } + else { data->z[i] = z2; - data->x[i] = x2; - } - + data->x[i] = x2; + } + } } @@ -5120,13 +5755,15 @@ void SeparateCartesianBoundaries(struct FemType *data,struct BoundaryType *bound void SeparateMainaxisBoundaries(struct FemType *data,struct BoundaryType *bound) { - int i,j,k,l,maxtype,addtype = 0,elemsides; + int i,j,k,l,maxtype,addtype,elemsides; int sideelemtype,sideind[MAXNODESD1]; int axistype[4],axishit[4],axissum,axismax,done; Real x,y,z,sx,sy,sz,sxx,syy,szz,dx,dy,dz; Real eps=1.0e-6; maxtype = 0; + addtype = 0; + for(j=0;jnoboundaries;j++) { if(!bound[j].created) continue; @@ -5231,7 +5868,6 @@ void SeparateMainaxisBoundaries(struct FemType *data,struct BoundaryType *bound) if(!done) { axissum = 0; axismax = 0; - addtype = 0; for(k=0;k<4;k++) { axissum += axishit[k]; @@ -5272,16 +5908,22 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, struct GridType *grid, struct FemType *data,struct BoundaryType *bound, int info) +/* Create mesh from 2D mesh either by extrusion or by rotation. + Also create the additional boundaries using automated numbering. */ { - int i,j,k,l,m,n,knot0,knot1,knot2 = 0,elem0,size,kmax,noknots,origtype; - int nonodes3d,nonodes2d; - int cellk,element,level,side,parent,parent2,layers,elemtype; - int material,material2,ind1,ind2,*indx,*topo; - int sideelemtype,sideind[MAXNODESD1],sidetype,maxsidetype,newbounds; - int refmaterial1[10],refmaterial2[10],refsidetype[10],indxlength; - Real z,*newx,*newy,*newz,corder[3]; +#define MAXNEWBC 200 + int i,j,k,l,m,n,knot0,knot1,knot2,elem0,size,kmax,noknots,origtype; + int nonodes3d,nonodes2d,bclevel,bcset; + int cellk,element,level,side,parent,parent2,layers,elemtype,material_too_large; + int material,material2,ind1,ind2; + int *indx=NULL,*topo=NULL; + int sideelemtype,sideind[MAXNODESD1],sidetype,minsidetype,maxsidetype,cummaxsidetype,newbounds; + int refmaterial1[MAXNEWBC],refmaterial2[MAXNEWBC],refsidetype[MAXNEWBC],indxlength; + Real z,*newx=NULL,*newy=NULL,*newz=NULL,corder[3]; Real meanx,meany; - + int layerbcoffset; + int usenames; + if(grid->rotate) SetElementDivisionCylinder(grid,info); else if(grid->dimension == 3) @@ -5295,7 +5937,9 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, data->dim = 3; - origtype = dataxy->elementtypes[1]; + origtype = 0; + for(i=1;i<=dataxy->noelements;i++) + origtype = MAX( origtype, dataxy->elementtypes[i]); if(origtype == 303) elemtype = 706; @@ -5309,7 +5953,7 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, printf("CreateKnotsExtruded: not implemented for elementtypes %d!\n",origtype); return; } - if(info) printf("Elementtype %d extruded to type %d.\n",origtype,elemtype); + printf("Maxium elementtype %d extruded to type %d.\n",origtype,elemtype); nonodes2d = origtype%100; data->maxnodes = nonodes3d = elemtype%100; @@ -5317,16 +5961,33 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, layers = 1; else layers = 2; + + /* Initialize the 3D mesh structure */ data->noknots = noknots = dataxy->noknots*(layers*grid->totzelems+1); data->noelements = dataxy->noelements * grid->totzelems; data->coordsystem = dataxy->coordsystem; + data->numbering = dataxy->numbering; data->noboundaries = dataxy->noboundaries; data->maxsize = dataxy->maxsize; data->minsize = dataxy->minsize; data->partitionexist = FALSE; data->periodicexist = FALSE; - data->connectexist = FALSE; + data->nodeconnectexist = FALSE; + data->elemconnectexist = FALSE; + + usenames = dataxy->bodynamesexist || dataxy->boundarynamesexist; + if( usenames ) { + if( grid->zmaterialmapexists ) { + printf("Cannot extrude names when there is a given material mapping!\n"); + usenames = FALSE; + } + else { + if(info) printf("Trying to maintain entity names in extrusion\n"); + } + } + + maxsidetype = 0; AllocateKnots(data); @@ -5345,58 +6006,90 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, newbounds += grid->rotateblocks; } + /* Initialize the boundaries of the 3D mesh */ for(j=0;jnoboundaries+newbounds;j++) { - if(j < data->noboundaries) - if(!boundxy[j].created) continue; + if(boundxy[j].created || j>=data->noboundaries) { + bound[j] = boundxy[j]; + bound[j].created = TRUE; - if(0) bound[j] = boundxy[j]; - bound[j].created = TRUE; - - if(j >= data->noboundaries) - size = dataxy->noelements; - else size = bound[j].nosides = boundxy[j].nosides * grid->totzelems; - - bound[j].coordsystem = COORD_CART3; - bound[j].side = Ivector(1,size); - bound[j].side2 = Ivector(1,size); - bound[j].material = Ivector(1,size); - bound[j].parent = Ivector(1,size); - bound[j].parent2 = Ivector(1,size); - bound[j].types = Ivector(1,size); - bound[j].normal = Ivector(1,size); - - for(i=1;i<=size;i++) { - bound[j].types[i] = 0; - bound[j].side[i] = 0; - bound[j].side2[i] = 0; - bound[j].parent[i] = 0; - bound[j].parent2[i] = 0; - bound[j].material[i] = 0; - bound[j].normal[i] = 1; + if(j >= data->noboundaries) size = dataxy->noelements; + + bound[j].coordsystem = COORD_CART3; + bound[j].side = Ivector(1,size); + bound[j].side2 = Ivector(1,size); + bound[j].material = Ivector(1,size); + bound[j].parent = Ivector(1,size); + bound[j].parent2 = Ivector(1,size); + bound[j].types = Ivector(1,size); + bound[j].normal = Ivector(1,size); + + for(i=1;i<=size;i++) { + bound[j].types[i] = 0; + bound[j].side[i] = 0; + bound[j].side2[i] = 0; + bound[j].parent[i] = 0; + bound[j].parent2[i] = 0; + bound[j].material[i] = 0; + bound[j].normal[i] = 1; + } } } - + if(info) printf("Allocated for %d new BC lists\n",j); + knot0 = 0; knot1 = layers*dataxy->noknots; - if(layers == 2) knot2 = dataxy->noknots; + if(layers == 2) + knot2 = dataxy->noknots; + else + knot2 = 0; elem0 = 0; level = 0; + material_too_large = 0; - + /* Set the element topology of the extruded mesh */ for(cellk=1;cellk <= grid->zcells ;cellk++) { kmax = grid->zelems[cellk]; - + for(k=1;k<=kmax; k++) { if(0) printf("elem0=%d knot0=%d knot1=%d\n",elem0,knot0,knot1); level++; for(element=1;element <= dataxy->noelements;element++) { - if(dataxy->material[element] < grid->zfirstmaterial[cellk]) continue; - if(dataxy->material[element] > grid->zlastmaterial[cellk]) continue; + + origtype = dataxy->elementtypes[element]; + nonodes2d = origtype % 100; + if(origtype == 303) + elemtype = 706; + else if(origtype == 404) + elemtype = 808; + else if(origtype == 408) + elemtype = 820; + else if(origtype == 409) + elemtype = 827; + + if( grid->zmaterialmapexists ) { + material = dataxy->material[element]; + if(material > grid->maxmaterial ) { + material_too_large += 1; + continue; + } + material = grid->zmaterialmap[cellk][material]; + if(material <= 0 ) continue; + } + else { + if(dataxy->material[element] < grid->zfirstmaterial[cellk]) continue; + if(dataxy->material[element] > grid->zlastmaterial[cellk]) continue; + + if(grid->zmaterial[cellk]) + material = grid->zmaterial[cellk]; + else + material = dataxy->material[element]; + } + if(grid->rotate) { meanx = 0.0; for(i=0;inoelements+element] = elem0; - - data->elementtypes[elem0] = elemtype; - - if(grid->zmaterial[cellk]) - data->material[elem0] = grid->zmaterial[cellk]; - else - data->material[elem0] = dataxy->material[element]; + indx[(level-1)*dataxy->noelements+element] = elem0; + data->elementtypes[elem0] = elemtype; + data->material[elem0] = material; if(elemtype == 706) { for(i=0;i<3;i++) { @@ -5452,10 +6140,19 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, } } data->noelements = elem0; - if(info) printf("Extruded mesh has %d elements in %d levels.\n",elem0,level); + printf("Extruded mesh has %d elements in %d levels.\n",elem0,level); + printf("Simple extrusion would have %d elements\n",level*dataxy->noelements); + + if( material_too_large > 0 ) { + printf("Material index exceeded %d the size of material permutation table (%d)!\n", + material_too_large,grid->maxmaterial); + printf("Give the max material with > Extruded Max Material < , if needed\n"); + } + + if(elem0 == 0) bigerror("No use to continue with zero elements!"); - /* Set the element coordinates. */ + /* Set the nodal coordinates of the extruded mesh. */ knot0 = 0; for(cellk=1;cellk <= grid->zcells ;cellk++) { @@ -5517,19 +6214,22 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, } } - + /* Perform cylindrical coordinate transformation */ if(grid->rotate) CylindricalCoordinateTransformation(data,grid->rotateradius1, grid->rotateradius2,grid->rotatecartesian); - maxsidetype = 0; + cummaxsidetype = 0; sidetype = 0; - - - /* Extrude the 2D boundary conditions. */ + /* Extrude the 2D boundary conditions. Initially BCs typically have parents with + different material. If due to selective extrusion they become the same then + the extruded BC does not have that component. */ for(j=0;jnoboundaries;j++) { if(!bound[j].created) continue; + + maxsidetype = 0; + minsidetype = INT_MAX; side = 0; level = 0; @@ -5537,12 +6237,9 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, for(k=1;k<=grid->zelems[cellk]; k++) { level++; -#if 0 - printf("level=%d j=%d side=%d\n",level,j,side); -#endif - for(i=1;i<=boundxy[j].nosides;i++){ - + + /* Find the parent element indexes and the corresponding material indexes */ ind1 = (level-1)*dataxy->noelements + boundxy[j].parent[i]; parent = indx[ind1]; @@ -5559,17 +6256,17 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, if(parent2) material2 = data->material[parent2]; else material2 = 0; -#if 0 - printf("ind=[%d %d] parent=[%d %d] material=[%d %d]\n", - ind1,ind2,parent,parent2,material,material2); -#endif - if((parent || parent2) && (material != material2)) { side++; + if(!parent & !parent2) printf("no parent = %d %d %d %d %d\n",parent,parent2,ind1,ind2,level); + sidetype = boundxy[j].types[i]; bound[j].types[side] = sidetype; + maxsidetype = MAX( maxsidetype, sidetype ); + minsidetype = MIN( minsidetype, sidetype ); + if(parent) { bound[j].parent[side] = parent; bound[j].parent2[side] = parent2; @@ -5589,83 +6286,135 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, } } bound[j].nosides = side; - if(sidetype > maxsidetype) maxsidetype = sidetype; - printf("Extruded BC %d of type %d was created with %d sides.\n", - j,sidetype,side); + cummaxsidetype = MAX( maxsidetype, cummaxsidetype ); + + if(info) { + if(side) + printf("Extruded BCs list %d of types [%d,%d] has %d elements.\n", + j,minsidetype,maxsidetype,side); + else + printf("Extruded BCs list %d has no elements!\n",j); + } + } + bcset = dataxy->noboundaries-1; + + + if( usenames ) { + for(i=1;i< MAXBODIES;i++) + strcpy(data->bodyname[i],dataxy->bodyname[i]); + for(i=1;i< MAXBOUNDARIES;i++) + strcpy(data->boundaryname[i],dataxy->boundaryname[i]); + data->bodynamesexist = TRUE; + data->boundarynamesexist = TRUE; + } + + /* Find the BCs that are created for constant z-levels. + Here number all parent combinations so that each pair gets + a new BC index. They are numbered by their order of appearance. */ + layerbcoffset = grid->layerbcoffset; + if(grid->layeredbc) { + if( !layerbcoffset ) sidetype = maxsidetype; + /* Find the BCs between layers. */ if(grid->dimension == 3 || grid->rotatecartesian) { side = 0; level = 0; - j--; - + bclevel = 0; + + + /* Go through extruded cells */ for(cellk=1;cellk <= grid->zcells ;cellk++) { int swap,redo; redo = FALSE; redolayer: - + maxsidetype = 0; + minsidetype = INT_MAX; + + /* Go through element layers within cells */ for(k=1;k<=grid->zelems[cellk]; k++) { level++; if(!(k == 1) && !(cellk == grid->zcells && k==grid->zelems[cellk])) continue; + /* Last cell in case of last just one element layer gives rise to two BCs */ if(cellk == grid->zcells && k == grid->zelems[cellk]) { if(grid->zelems[cellk] == 1) redo = TRUE; - else + else { level++; + } } - - if(grid->rotatecartesian && cellk%2 == 1) continue; + + if(grid->rotatecartesian && cellk % 2 == 1) continue; if(grid->rotatecartesian && k != 1) continue; - - for(i=0;i<10;i++) { - refmaterial1[i] = 0; - refmaterial2[i] = 0; - refsidetype[i] = 0; + + /* If layred bc offset is defined then the BCs are numbered deterministically + otherwise there is a complicated method of defining the BC index so that + indexes would be used in order. */ + if(!layerbcoffset) { + for(i=0;inoelements;i++){ - ind1 = (level-2)*dataxy->noelements+i; + origtype = dataxy->elementtypes[i]; + nonodes2d = origtype % 100; + + if(origtype == 303) + elemtype = 706; + else if(origtype == 404) + elemtype = 808; + else if(origtype == 408) + elemtype = 820; + else if(origtype == 409) + elemtype = 827; + + /* Check the parent elements of the layers. Only create a BC if the parents are + different. */ + ind1 = (level-2)*dataxy->noelements + i; if(ind1 < 1) parent = 0; else parent = indx[ind1]; - ind2 = (level-1)*dataxy->noelements+i; + ind2 = (level-1)*dataxy->noelements + i; if(ind2 > indxlength) parent2 = 0; else parent2 = indx[ind2]; + /* If only 2nd parent is given swap the order */ if(parent == 0 && parent2 != 0) { - parent = parent2; + parent = parent2; parent2 = 0; swap = 1; } - else + else { swap = 0; - + } + if(!parent) continue; + /* Get the materials related to the parents */ material = data->material[parent]; if(parent2) material2 = data->material[parent2]; else material2 = 0; -#if 0 - printf("level=%d ind=[%d %d] parent=[%d %d] material=[%d %d] swap=%d\n", - level,ind1,ind2,parent,parent2,material,material2,swap); -#endif - if(grid->rotatecartesian && !material2) { if(origtype == 303) GetElementSide(parent,4-swap,1,data,sideind,&sideelemtype); else GetElementSide(parent,5-swap,1,data,sideind,&sideelemtype); @@ -5693,89 +6442,118 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, for(n=0;nycells && grid->y[n]+1.0e-12 < meany;n++); material2 = grid->structure[n][m+1]; } -#if 0 - printf("cellk=%d meanx=%.3lg meany=%.3lg material2=%d m=%d n=%d\n", - cellk,meanx,meany,material2,m,n); -#endif } - - if(material != material2) { - + /* Create bc index only if the materials are different */ + if(material != material2) { side++; - bound[j].nosides = side; - bound[j].parent[side] = parent; - bound[j].parent2[side] = parent2; - bound[j].material[side] = material; + + bound[bcset].nosides = side; + bound[bcset].parent[side] = parent; + bound[bcset].parent2[side] = parent2; + bound[bcset].material[side] = material; if(origtype == 303) { - bound[j].side[side] = 4-swap; - bound[j].side2[side] = 3+swap; + bound[bcset].side[side] = 4-swap; + bound[bcset].side2[side] = 3+swap; } else { - bound[j].side[side] = 5-swap; - bound[j].side2[side] = 4+swap; + bound[bcset].side[side] = 5-swap; + bound[bcset].side2[side] = 4+swap; } - for(m=0;m<10;m++) { - if(refmaterial1[m] == material && refmaterial2[m] == material2) { - break; - } - else if(refmaterial1[m] == 0 && refmaterial2[m] == 0) { - refmaterial1[m] = material; - refmaterial2[m] = material2; - sidetype++; - refsidetype[m] = sidetype; - break; + /* Simple and deterministic, and complex and continuous numbering */ + if(layerbcoffset) { + sidetype = bclevel * layerbcoffset + dataxy->material[i]; + bound[bcset].types[side] = sidetype; + maxsidetype = MAX( sidetype, maxsidetype ); + minsidetype = MIN( sidetype, minsidetype ); + } + else { + for(m=0;mboundaryname[refsidetype[m]],"%s%s", + dataxy->bodyname[dataxy->material[i]],"_Start"); + else if( cellk == grid->zcells ) + sprintf(data->boundaryname[refsidetype[m]],"%s%s", + dataxy->bodyname[dataxy->material[i]],"_End"); + else + sprintf(data->boundaryname[refsidetype[m]],"%s%s%d", + dataxy->bodyname[dataxy->material[i]],"_Level",bclevel); } + + } - bound[j].types[side] = refsidetype[m]; + } } - printf("BC %d on layer %d was created with %d sides.\n",j,level,side); - if(sidetype > maxsidetype) maxsidetype = sidetype; + if(info) { + if(side) + printf("Layer BCs list %d of types [%d,%d] has %d elements.\n", + bcset,minsidetype,maxsidetype,side); + else + printf("Layer BCs list %d has no elements!\n",bcset); + } - if(redo == TRUE) goto redolayer; + if(redo == TRUE) { + goto redolayer; + } } } - j++; } } - /* Create four additional boundaries that may be used to force symmetry constraints. These are only created if the object is only partially rotated. */ + bcset++; if(grid->rotate && grid->rotateblocks < 4) { int o,p; + int blocks, maxradi,addtype; + Real eps,fii,rad,meanrad,maxrad,xc,yc,dfii,fii0,rads[4],fiis[4]; + o = p = 0; + eps = 1.0e-3; + blocks = grid->rotateblocks; for(element=1;element<=data->noelements;element++) { - int blocks, maxradi = 0,addtype; - Real eps,fii,rad,meanrad,maxrad,xc,yc,dfii,fii0,rads[4],fiis[4]; - - eps = 1.0e-3; - blocks = grid->rotateblocks; for(side=0;side<6;side++) { GetElementSide(element,side,1,data,&sideind[0],&sideelemtype); - + meanrad = 0.0; maxrad = 0.0; + maxradi = 0; for(i=0;i<4;i++) { xc = data->x[sideind[i]]; yc = data->y[sideind[i]]; rad = sqrt(yc*yc+xc*xc); - //fii = 2*atan2(yc,xc)/M_PI; /* Map fii to [0 4] */ - fii = 2*atan2(yc,xc)/FM_PI; /* Map fii to [0 4] */ + fii = 2*atan2(yc,xc)/M_PI; /* Map fii to [0 4] */ rads[i] = rad; fiis[i] = fii; @@ -5815,38 +6593,36 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, else addtype = 3; } - - if( addtype >= 0) { - bound[j+addtype].nosides++; - k = bound[j+addtype].nosides; - bound[j+addtype].side[k] = side; - bound[j+addtype].parent[k] = element; - bound[j+addtype].types[k] = sidetype+addtype+1; + + if( addtype >= 0) { + bound[bcset+addtype].nosides++; + k = bound[bcset+addtype].nosides; + bound[bcset+addtype].side[k] = side; + bound[bcset+addtype].parent[k] = element; + bound[bcset+addtype].types[k] = sidetype+addtype+1; } } } - printf("Symmetry BCs [%d %d %d %d] have [%d %d %d %d] sides.\n", - j,j+1,j+2,j+3,bound[j].nosides,bound[j+1].nosides, - bound[j+2].nosides,bound[j+3].nosides); - for(l=0;l<4;l++) { - if(bound[j+l].nosides == 0) - bound[j+l].created = FALSE; - else - bound[j+l].created = TRUE; + for(addtype=0;addtype<4;addtype++) { + l = bcset+addtype; + if(bound[l].nosides == 0) { + bound[l].created = FALSE; + } + else { + bound[l].created = TRUE; + if(info) { + if(bound[l].nosides) + printf("Symmetry BCs list %d of type %d has %d elements.\n", + l,sidetype+addtype+1,bound[l].nosides); + else + printf("Symmetry BCs list %d has no elements!\n",l); + } + } } - j += 4; - } - - data->noboundaries = j+1; - -#if 0 - for(i=0;inoboundaries = bcset+1; /* Renumber the element nodes so that all integers are used. @@ -5856,7 +6632,7 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, indx[i] = 0; for(element=1;element<=data->noelements;element++) { - nonodes3d = data->elementtypes[element]%100; + nonodes3d = data->elementtypes[element] % 100; for(i=0;itopology[element][i]] = 1; } @@ -5887,20 +6663,19 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, data->noknots = j; for(element=1;element<=data->noelements;element++) { - nonodes3d = data->elementtypes[element]%100; + nonodes3d = data->elementtypes[element] % 100; for(i=0;itopology[element][i] = indx[data->topology[element][i]]; } } - if(grid->rotate) { ReorderElements(data,bound,FALSE,corder,info); CylindricalCoordinateImprove(data,grid->rotateimprove, grid->rotateradius1,grid->rotateradius2); - if(grid->rotatecurve) + if(0 && grid->rotatecurve) CylindricalCoordinateCurve(data,grid->curvezet, grid->curverad,grid->curveangle); @@ -5915,6 +6690,31 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, data->noelements,data->noknots); free_Ivector(indx,0,indxlength); + + + /* Enforce constant helicity for the mesh if requested */ + if( grid->zhelicityexists ) { + Real helicity,fii,x,y,z,minz,maxz; + + helicity = (M_PI/180.0)*grid->zhelicity; + + minz = maxz = data->z[1]; + for(i=1;i<=data->noknots;i++) { + minz = MIN(minz,data->z[i]); + maxz = MAX(maxz,data->z[i]); + } + for(i=1;i<=data->noknots;i++) { + x = data->x[i]; + y = data->y[i]; + z = data->z[i]; + fii = helicity*(z-minz)/(maxz-minz); + + data->x[i] = cos(fii)*x - sin(fii)*y; + data->y[i] = sin(fii)*x + cos(fii)*y; + } + if(info) printf("Applied helicity of %12.5le degrees\n",grid->zhelicity); + } + } @@ -5922,8 +6722,9 @@ void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, void ReduceElementOrder(struct FemType *data,int matmin,int matmax) /* Reduces the element order at material interval [matmin,matmax] */ { - int i,j,element,material,elemcode1,elemcode2,maxnode,*indx,reduced; - Real *newx,*newy,*newz; + int i,j,element,material,elemcode1,elemcode2,maxnode,reduced; + int *indx=NULL; + Real *newx=NULL,*newy=NULL,*newz=NULL; indx = Ivector(0,data->noknots); for(i=0;i<=data->noknots;i++) @@ -5937,6 +6738,8 @@ void ReduceElementOrder(struct FemType *data,int matmin,int matmax) if(material >= matmin && material <= matmax) elemcode2 = 101*(elemcode1/100); if(elemcode2 == 505) elemcode2 = 504; /* tetrahedron */ + else if(elemcode2 == 606) elemcode2 = 605; /* pyramid */ + else if(elemcode2 == 707) elemcode2 = 706; /* prism */ #if 0 printf("element=%d codes=[%d,%d]\n",element,elemcode1,elemcode2); printf("mat=%d interval=[%d,%d]\n",material,matmin,matmax); @@ -5993,8 +6796,8 @@ void MergeElements(struct FemType *data,struct BoundaryType *bound, { int i,j,k,l; int noelements,noknots,newnoknots,nonodes; - int *mergeindx,*doubles; - Real *newx,*newy,*newz; + int *mergeindx=NULL,*doubles=NULL; + Real *newx=NULL,*newy=NULL,*newz=NULL; Real cx,cy,cz,dx,dy,dz,cdist,dist; ReorderElements(data,bound,manual,corder,TRUE); @@ -6036,6 +6839,7 @@ void MergeElements(struct FemType *data,struct BoundaryType *bound, dx = data->x[i] - data->x[j]; dy = data->y[i] - data->y[j]; dz = data->z[i] - data->z[j]; + if(fabs(cx*dx+cy*dy+cz*dz) > eps) break; dist = dx*dx + dy*dy + dz*dz; @@ -6074,11 +6878,6 @@ void MergeElements(struct FemType *data,struct BoundaryType *bound, newz[mergeindx[i]] = data->z[i]; } -#if 0 - for(i=1;i<=noknots;i++) - printf("i=%d indx=%d merge=%d\n",i,indx[i],mergeindx[i]); -#endif - free_Rvector(data->x,1,data->noknots); free_Rvector(data->y,1,data->noknots); free_Rvector(data->z,1,data->noknots); @@ -6161,21 +6960,124 @@ void MergeBoundaries(struct FemType *data,struct BoundaryType *bound,int *double +void IsoparametricElements(struct FemType *data,struct BoundaryType *bound, + int bcstoo,int info) +{ + int i,j,k; + int noelements,noknots; + int element,side,sideelemtype,sidenodes,elemtype; + int *bcindx=NULL,*topo=NULL,sideind[MAXNODESD1]; + Real *x=NULL,*y=NULL,*z=NULL; + + noelements = data->noelements; + noknots = data->noknots; + x = data->x; + y = data->y; + z = data->z; + + bcindx = Ivector(1,noknots); + for(i=1;i<=noknots;i++) + bcindx[i] = FALSE; + + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound[j].created) continue; + + for(i=1; i <= bound[j].nosides; i++) { + element = bound[j].parent[i]; + side = bound[j].side[i]; + + GetElementSide(element,side,1,data,sideind,&sideelemtype); + + sidenodes = sideelemtype%100; + + for(k=0;kelementtypes[j]; + topo = data->topology[j]; + + if(elemtype == 306) { + for(i=0;i<3;i++) { + if(!bcindx[topo[i+3]]) { + x[topo[i+3]] = 0.5*(x[topo[i]]+x[topo[(i+1)%3]]); + y[topo[i+3]] = 0.5*(y[topo[i]]+y[topo[(i+1)%3]]); + } + } + + } + else if(elemtype == 310) { + for(i=0;i<3;i++) { + if(!bcindx[topo[2*i+3]]) { + x[topo[2*i+3]] = (2.0*x[topo[i]]+1.0*x[topo[(i+1)%3]])/3.0; + x[topo[2*i+4]] = (1.0*x[topo[i]]+2.0*x[topo[(i+1)%3]])/3.0; + y[topo[2*i+3]] = (2.0*y[topo[i]]+1.0*y[topo[(i+1)%3]])/3.0; + y[topo[2*i+4]] = (1.0*y[topo[i]]+2.0*y[topo[(i+1)%3]])/3.0; + } + } + x[topo[9]] = (x[topo[0]]+x[topo[1]]+x[topo[2]])/3.0; + y[topo[9]] = (y[topo[0]]+y[topo[1]]+y[topo[2]])/3.0; + } + else if(elemtype == 408 || elemtype == 409) { + for(i=0;i<4;i++) { + if(!bcindx[topo[i+4]]) { + x[topo[i+4]] = 0.5*(x[topo[i]]+x[topo[(i+1)%4]]); + y[topo[i+4]] = 0.5*(y[topo[i]]+y[topo[(i+1)%4]]); + } + } + if(elemtype == 409) { + x[topo[8]] = 0.25*(x[topo[0]]+x[topo[1]]+x[topo[2]]+x[topo[3]]); + y[topo[8]] = 0.25*(y[topo[0]]+y[topo[1]]+y[topo[2]]+y[topo[3]]); + } + } + else if(elemtype == 412 || elemtype == 416) { + for(i=0;i<4;i++) { + if(!bcindx[topo[2*i+4]]) { + x[topo[2*i+4]] = (2.0*x[topo[i]]+1.0*x[topo[(i+1)%4]])/3.0; + x[topo[2*i+5]] = (1.0*x[topo[i]]+2.0*x[topo[(i+1)%4]])/3.0; + y[topo[2*i+4]] = (2.0*y[topo[i]]+1.0*y[topo[(i+1)%4]])/3.0; + y[topo[2*i+5]] = (1.0*y[topo[i]]+2.0*y[topo[(i+1)%4]])/3.0; + } + } + if(elemtype == 416) { + Real xmean,ymean; + xmean = (x[topo[0]]+x[topo[1]]+x[topo[2]]+x[topo[3]])/4.0; + ymean = (y[topo[0]]+y[topo[1]]+y[topo[2]]+y[topo[3]])/4.0; + for(i=0;i<4;i++) { + x[topo[11+i]] = (2.*xmean + 1.0*x[i]) / 3.0; + y[topo[11+i]] = (2.*ymean + 1.0*y[i]) / 3.0; + } + } + } + else { + printf("IsoparamametricElements: Not implemented for elementtype %d\n",elemtype); + } + } + + if(info) printf("The elements were forced to be isoparametric\n"); +} + + void ElementsToBoundaryConditions(struct FemType *data, struct BoundaryType *bound,int retainorphans,int info) { - int i,j,k,l,sideelemtype,sideelemtype2,elemind,elemind2,parent,sideelem,sameelem; + int i,j,k,l,sideelemtype,sideelemtype2,elemind,elemind2,sideelem,sameelem; int sideind[MAXNODESD1],sideind2[MAXNODESD1],elemsides,side,hit,same,minelemtype; int sidenodes,sidenodes2,maxelemtype,elemtype,elemdim,sideelements,material; - int *moveelement,*parentorder,*possible,**invtopo; + int *moveelement=NULL,*parentorder=NULL,*possible=NULL,**invtopo=NULL; int noelements,maxpossible,noknots,maxelemsides,twiceelem,sideelemdim; - int debug,unmoved,removed,elemhits; - int notfound,*notfounds; + int debug,unmoved,removed,elemhits,loopdim,elemdim2,lowdimbulk; + int notfound,*notfounds=NULL; - if(info) printf("Making elements to boundary conditions\n"); - + if(info) { + printf("Moving bulk elements to boundary elements\n"); + if(0) printf("Trying to retain orphans: %d\n",retainorphans); + } + for(j=0;j < MAXBOUNDARIES;j++) bound[j].created = FALSE; for(j=0;j < MAXBOUNDARIES;j++) @@ -6191,8 +7093,11 @@ void ElementsToBoundaryConditions(struct FemType *data, if(info) printf("Trailing bulk elementtype is %d\n",minelemtype); elemdim = GetElementDimension(maxelemtype); - if( elemdim - GetElementDimension(minelemtype) == 0) return; - + if( elemdim - GetElementDimension(minelemtype) == 0) { + if(info) printf("No lower dimensional elements present!\n"); + return; + } + moveelement = Ivector(1,noelements); sideelements = 0; @@ -6201,20 +7106,13 @@ void ElementsToBoundaryConditions(struct FemType *data, unmoved = 0; removed = 0; notfound = 0; + lowdimbulk = 0; for(i=1;i<=noelements;i++) { moveelement[i] = FALSE; - elemsides = data->elementtypes[i]/100; - - if(elemsides > 4) - sideelemdim = 3; - else if(elemsides > 2) - sideelemdim = 2; - else if(elemsides > 1) - sideelemdim = 1; - else if(elemsides == 1) - sideelemdim = 0; + sideelemdim = GetElementDimension(data->elementtypes[i]); + /* Lower dimensional elements are candidates to become BC elements */ moveelement[i] = elemdim - sideelemdim; if(moveelement[i]) sideelements++; } @@ -6224,10 +7122,13 @@ void ElementsToBoundaryConditions(struct FemType *data, AllocateBoundary(bound,sideelements); + /* Compute maximum number of hits for inverse topology */ possible = Ivector(1,noknots); for(i=1;i<=noknots;i++) possible[i] = 0; for(elemind=1;elemind <= data->noelements;elemind++) { - if(moveelement[elemind]) continue; + /* if(moveelement[elemind]) continue; */ + elemtype = data->elementtypes[elemind]; + if(elemtype < 200 ) continue; for(i=0;ielementtypes[elemind]%100;i++) { j = data->topology[elemind][i]; possible[j] += 1; @@ -6236,11 +7137,12 @@ void ElementsToBoundaryConditions(struct FemType *data, j = 1; maxpossible = possible[1]; - for(i=1;i<=noknots;i++) + for(i=1;i<=noknots;i++) { if(maxpossible < possible[i]) { maxpossible = possible[i]; j = i; } + } if(info) printf("Node %d belongs to maximum of %d elements\n",j,maxpossible); /* Make a table showing to which elements a node belongs to @@ -6251,8 +7153,9 @@ void ElementsToBoundaryConditions(struct FemType *data, invtopo[i][j] = 0; for(elemind=1;elemind <= data->noelements;elemind++) { - if(moveelement[elemind]) continue; + /* if(moveelement[elemind]) continue; */ elemtype = data->elementtypes[elemind]; + if(elemtype < 200 ) continue; for(i=0;itopology[elemind][i]; for(l=1;invtopo[k][l];l++); @@ -6266,80 +7169,109 @@ void ElementsToBoundaryConditions(struct FemType *data, debug = FALSE; - for(elemind=1;elemind <= data->noelements;elemind++) { - - if(!moveelement[elemind]) continue; + /* Go through boundary element candidates starting from higher dimension */ + for(loopdim=elemdim-1;loopdim>=0;loopdim--) { - same = FALSE; - sideelemtype = data->elementtypes[elemind]; + if(0) printf("loopdim = %d\n",loopdim); - sidenodes = sideelemtype % 100; - for(i=0;itopology[elemind][i]; - elemhits = 0; - - for(l=1;l<=maxpossible;l++) { - elemind2 = invtopo[sideind[0]][l]; - - if(!elemind2) continue; - - elemtype = data->elementtypes[elemind2]; - hit = 0; - for(i=0;itopology[elemind2][j]) hit++; + for(elemind=1;elemind <= data->noelements;elemind++) { - if(hit < sidenodes) continue; - - if(hit > sidenodes) printf("Strange: elemhits %d vs. elemnodes %d\n",hit,sidenodes); - if(hit >= sidenodes) elemhits++; + if(!moveelement[elemind]) continue; - for(side=0;side<=100;side++) { - - if(debug) printf("elem1=%d l=%d elem2=%d side=%d\n",elemind,l,elemind2,side); + same = FALSE; + sideelemtype = data->elementtypes[elemind]; + + /* Only check the elements that have right dimension */ + sideelemdim = GetElementDimension(sideelemtype); + if(sideelemdim != loopdim ) continue; + + sidenodes = sideelemtype % 100; + for(i=0;itopology[elemind][i]; + elemhits = 0; - GetElementSide(elemind2,side,1,data,&sideind2[0],&sideelemtype2); + if(debug) printf("Finding elem: %d %d %d\n",elemind,sideelemtype,sideelemdim); - if(debug) printf("elemtype=%d sidelemtype=%d %d\n", - elemtype,sideelemtype,sideelemtype2); + + for(l=1;l<=maxpossible;l++) { + elemind2 = invtopo[sideind[0]][l]; + + if(!elemind2) continue; - if(sideelemtype2 == 0 ) break; - if(sideelemtype2 < 300 && sideelemtype > 300) break; - if(sideelemtype2 < 200 && sideelemtype > 200) break; + /* The parent should be an element that will not become BC element */ + if(moveelement[elemind2]) continue; + + elemtype = data->elementtypes[elemind2]; + elemdim2 = GetElementDimension(elemtype); - sidenodes2 = sideelemtype2 % 100; - if(sidenodes != sidenodes2) continue; - if(sidenodes2 == 1 && sidenodes > 1) break; + /* Owner element should have highger dimension */ + if(elemdim2 <= sideelemdim ) continue; hit = 0; - for(i=0;itopology[elemind2][j]) hit++; + if(hit < sidenodes) continue; + + if(hit > sidenodes) printf("Strange: elemhits %d vs. elemnodes %d\n",hit,sidenodes); + if(hit >= sidenodes) elemhits++; + + for(side=0;side<=100;side++) { + if(0) printf("elem1=%d l=%d elem2=%d side=%d\n",elemind,l,elemind2,side); + + GetElementSide(elemind2,side,1,data,&sideind2[0],&sideelemtype2); + + if(0) printf("elemtype=%d sidelemtype=%d %d\n", + elemtype,sideelemtype,sideelemtype2); + + if(sideelemtype2 == 0 ) break; + if(sideelemtype2 < 300 && sideelemtype > 300) break; + if(sideelemtype2 < 200 && sideelemtype > 200) break; + + sidenodes2 = sideelemtype2 % 100; + if(sidenodes != sidenodes2) continue; + if(sidenodes2 == 1 && sidenodes > 1) break; + + hit = 0; + for(i=0;iparent2[sideelem] = elemind2; - bound->side2[sideelem] = side; + bound->side2[sideelem] = side; + + if(debug) printf(" Found 2nd: %d %d %d\n",elemind,elemind2,side); goto foundtwo; } else { sideelem += 1; same = TRUE; - if(debug) printf("sideelem=%d %d %d\n",sideelem,side,elemind2); + if(debug) printf(" Found 1st: %d %d %d\n",elemind,elemind2,side); + bound->parent[sideelem] = elemind2; bound->side[sideelem] = side; bound->parent2[sideelem] = 0; - bound->side2[sideelem] = 0; + bound->side2[sideelem] = 0; material = data->material[elemind]; bound->types[sideelem] = material; + if(sidenodes == 2) { if((sideind[0]-sideind[1])*(sideind2[0]-sideind2[1])<0) bound->normal[sideelem] = -1; @@ -6352,41 +7284,55 @@ void ElementsToBoundaryConditions(struct FemType *data, strncpy(data->boundaryname[material],"bnry",4); } + /* Only try to find two parents if the boundary element is one degree smaller than maximum dimension */ if(moveelement[elemind] > 1) goto foundtwo; } } - } - - if(!same) { - - if(0) { - printf("element: index = %d type = %d nodes = %d elemhits = %d\n", - elemind,sideelemtype,sidenodes,elemhits); - printf(" inds ="); - for(i=0;inosides = sideelem; + printf("Removing %d lower dimensional elements from the element list\n",removed); if(notfound) { printf("************************** WARNING **********************\n"); @@ -6427,12 +7378,11 @@ void ElementsToBoundaryConditions(struct FemType *data, } } - - bound->nosides = sideelem; - - - /* Reorder remaining master elements */ + /* Reorder remaining bulk elements */ parentorder = Ivector(1,noelements); + for(i=1;i<=noelements;i++) + parentorder[i] = 0; + j = 0; for(i=1;i<=noelements;i++) { if(moveelement[i] == 0) { @@ -6440,25 +7390,35 @@ void ElementsToBoundaryConditions(struct FemType *data, j++; parentorder[i] = j; - data->material[j] = data->material[i]; - data->elementtypes[j] = data->elementtypes[i]; - - for(l=0;ltopology[j][l] = data->topology[i][l]; + + if(debug) printf("Bulk is: %d %d\n",i,j); + + if( i != j ) { + data->material[j] = data->material[i]; + data->elementtypes[j] = data->elementtypes[i]; + for(l=0;ltopology[j][l] = data->topology[i][l]; + } } - else - parentorder[i] = 0; } data->noelements = j; - if(info) printf("Parent elements were reordered up to indx %d.\n",j); + if(info) printf("Parent elements were reordered up to index %d.\n",j); /* Reorder boundary to point at the new arrangement of master elements */ for(i=1;i<=bound->nosides;i++) { - if(bound->parent[i]) bound->parent[i] = parentorder[bound->parent[i]]; + if( !parentorder[bound->parent[i]] ) { + printf("Zero reorder: %d %d %d\n",i,bound->parent[i],bound->side[i]); + bigerror("Sorry folks!"); + } + + if(bound->parent[i]) bound->parent[i] = parentorder[bound->parent[i]]; if(bound->parent2[i]) bound->parent2[i] = parentorder[bound->parent2[i]]; - } + + GetElementSide(bound->parent[i],bound->side[i],1,data,&sideind2[0],&sideelemtype2); + if(0) GetBoundaryElement(i,&bound[j],data,&sideind2[0],&sideelemtype2); + } if(info) printf("Moved %d elements (out of %d) to new positions\n",j,noelements); @@ -6469,21 +7429,256 @@ void ElementsToBoundaryConditions(struct FemType *data, free_Imatrix(invtopo,1,noknots,1,maxpossible); if(notfound) free_Ivector(notfounds,1,noelements); - if(info) printf("All done\n"); + if(0) printf("All done\n"); return; } +int SideAndBulkMappings(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info) +{ + int i,j,l,currenttype; + + + if(eg->sidemappings) { + for(l=0;lsidemappings;l++) + if(info) printf("Setting boundary types between %d and %d to %d\n", + eg->sidemap[3*l],eg->sidemap[3*l+1],eg->sidemap[3*l+2]); + + for(j=0;j < MAXBOUNDARIES;j++) { + if(!bound[j].created) continue; + + for(i=1; i <= bound[j].nosides; i++) { + if(currenttype = bound[j].types[i]) { + for(l=0;lsidemappings;l++) { + if(currenttype >= eg->sidemap[3*l] && currenttype <= eg->sidemap[3*l+1]) { + bound[j].types[i] = eg->sidemap[3*l+2]; + currenttype = -1; + } + } + } + } + } + if(info) printf("Renumbering boundary types finished\n"); + } + + if(eg->bulkmappings) { + for(l=0;lbulkmappings;l++) + if(info) printf("Setting material types between %d and %d to %d\n", + eg->bulkmap[3*l],eg->bulkmap[3*l+1],eg->bulkmap[3*l+2]); + for(j=1;j<=data->noelements;j++) { + currenttype = data->material[j]; + for(l=0;lbulkmappings;l++) { + if(currenttype >= eg->bulkmap[3*l] && currenttype <= eg->bulkmap[3*l+1]) { + data->material[j] = eg->bulkmap[3*l+2]; + currenttype = -1; + } + } + } + if(info) printf("Renumbering material indexes finished\n"); + } + return(0); +} + + + +int SideAndBulkBoundaries(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info) +{ + int l; + int *boundnodes,noboundnodes; + boundnodes = Ivector(1,data->noknots); + + if(eg->bulkbounds) { + for(l=0;lbulkbounds;l++) { + FindBulkBoundary(data,eg->bulkbound[3*l],eg->bulkbound[3*l+1], + boundnodes,&noboundnodes,info); + FindNewBoundaries(data,bound,boundnodes,eg->bulkbound[3*l+2],1,info); + } + } + if(eg->boundbounds) { + for(l=0;lboundbounds;l++) { + FindBoundaryBoundary(data,bound,eg->boundbound[3*l],eg->boundbound[3*l+1], + boundnodes,&noboundnodes,info); + FindNewBoundaries(data,bound,boundnodes,eg->boundbound[3*l+2],2,info); + } + } + free_Ivector(boundnodes,1,data->noknots); + + return(0); +} + + +void NodesToBoundaryChain(struct FemType *data,struct BoundaryType *bound, + int *bcinds,int *bctags,int nbc,int bccount, + int info) +{ + int i,j,k,l,sideelemtype,sideelemtype2,elemind,elemind2,sideelem,sameelem; + int sideind[MAXNODESD1],sideind2[MAXNODESD1],elemsides,side,hit,same,minelemtype; + int sidenodes,sidenodes2,elemtype,elemdim,sideelements,material; + int *possible=NULL,**invtopo=NULL; + int noelements,maxpossible,noknots,twiceelem,sideelemdim; + int elemhits,bci; + + + if(info) printf("Creating boundary elements from boundary nodes\n"); + + for(j=0;j < MAXBOUNDARIES;j++) + bound[j].created = FALSE; + for(j=0;j < MAXBOUNDARIES;j++) + bound[j].nosides = 0; + + noelements = data->noelements; + noknots = data->noknots; + + sideelements = nbc - bccount; + printf("Expected number of BC elements: %d\n",sideelements); + + AllocateBoundary(bound,sideelements); + + /* Calculate how may times a node apppears */ + possible = Ivector(1,noknots); + for(i=1;i<=noknots;i++) possible[i] = 0; + for(elemind=1;elemind <= data->noelements;elemind++) { + for(i=0;ielementtypes[elemind]%100;i++) { + j = data->topology[elemind][i]; + possible[j] += 1; + } + } + + j = 1; + maxpossible = possible[1]; + for(i=1;i<=noknots;i++) { + if(maxpossible < possible[i]) { + maxpossible = possible[i]; + j = i; + } + } + if(info) printf("Node %d belongs to maximum of %d elements\n",j,maxpossible); + + /* Make a table showing to which elements a node belongs to + Include only the potential parents which are not to be moved to BCs. */ + invtopo = Imatrix(1,noknots,1,maxpossible); + + for(i=1;i<=noknots;i++) + for(j=1;j<=maxpossible;j++) + invtopo[i][j] = 0; + + for(elemind=1;elemind <= data->noelements;elemind++) { + elemtype = data->elementtypes[elemind]; + for(i=0;itopology[elemind][i]; + for(l=1;invtopo[k][l];l++); /* Yes, this is really ok. We look for unset entry. */ + invtopo[k][l] = elemind; + } + } + + sideelem = 0; + sameelem = 0; + twiceelem = 0; + + /* These are here by construction because we are looking for a chain of nodes + and trying to create 202 elements of them! */ + sidenodes = 2; + sideelemtype = 202; + + for(bci=1;bcielementtypes[elemind2]; + hit = 0; + for(i=0;itopology[elemind2][j]) hit++; + + /* We must have all hits to have a chance of finding bc */ + if(hit < sidenodes) continue; + + elemhits++; + + /* Now find on which side the bc is */ + for(side=0;side<3;side++) { + GetElementSide(elemind2,side,1,data,&sideind2[0],&sideelemtype2); + if( sideelemtype2 != sideelemtype ) printf("This should not happen!\n"); + + hit = 0; + for(i=0;iparent2[sideelem] = elemind2; + bound->side2[sideelem] = side; + goto foundtwo; + } + else { + /* We haven't found parents for this bc elements yet */ + sideelem += 1; + same = TRUE; + bound->parent[sideelem] = elemind2; + bound->side[sideelem] = side; + bound->parent2[sideelem] = 0; + bound->side2[sideelem] = 0; + bound->types[sideelem] = material; + if(sidenodes == 2) { + if((sideind[0]-sideind[1])*(sideind2[0]-sideind2[1])<0) + bound->normal[sideelem] = -1; + } + } + } + } + foundtwo: + continue; + } + + if(twiceelem) printf("Found %d sides that were multiply given\n",twiceelem); + if(sameelem) printf("Found %d side elements that have two parents.\n",sameelem); + + + if(sideelem == sideelements) { + printf("Found correctly %d side elements.\n",sideelem); + } + else { + printf("Found %d side elements, could have found %d\n",sideelem,sideelements); + } + + bound->nosides = sideelem; + + free_Ivector(possible,1,noknots); + free_Imatrix(invtopo,1,noknots,1,maxpossible); + + return; +} + + + int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) { int i,j,i2,j2,dim; - int noknots,hit,tothits,dimvisited; - int *topbot = NULL,*indxper; - int botn,topn,*revindtop,*revindbot; - Real eps,dist = 0,dx,dy,dz,coordmax,coordmin; - Real *coord = NULL,*toparr,*botarr,epsmin; + int noknots,hit,tothits; + int *topbot=NULL,*indxper=NULL; + int botn,topn,*revindtop=NULL,*revindbot=NULL; + Real eps,dist,dx,dy,dz,coordmax,coordmin; + Real *coord=NULL,*toparr=NULL,*botarr=NULL,epsmin; if(data->dim < 3) periodicdim[2] = 0; @@ -6496,26 +7691,27 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) noknots = data->noknots; tothits = 0; - dimvisited = FALSE; data->periodicexist = TRUE; indxper = Ivector(1,noknots); data->periodic = indxper; - + topbot = Ivector(1,noknots); + + for(i=1;i<=noknots;i++) indxper[i] = i; - + for(dim=1;dim<=3;dim++) { if(!periodicdim[dim-1]) continue; - if(info) printf("Finding periodic nodes in dim=%d\n",dim); - + if(info) printf("Finding periodic nodes in direction %d\n",dim); + if(dim==1) coord = data->x; else if(dim==2) coord = data->y; - else if(dim==3) coord = data->z; - + else coord = data->z; + coordmax = coordmin = coord[1]; - + for(i=1;i<=data->noknots;i++) { if(coordmax < coord[i]) coordmax = coord[i]; if(coordmin > coord[i]) coordmin = coord[i]; @@ -6525,10 +7721,6 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) dim,coordmin,coordmax); if(coordmax-coordmin < 1.0e-10) continue; - - if(!dimvisited) { - topbot = Ivector(1,noknots); - } eps = 1.0e-5 * (coordmax-coordmin); topn = botn = 0; @@ -6572,7 +7764,6 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) revindbot[botn] = i; } } - if(data->dim == 2) { for(i=1;i<=botn;i++) { @@ -6580,21 +7771,21 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) hit = FALSE; for(i2=1;i2<=topn;i2++) { j2 = revindtop[i2]; - if(dim == 1) dist = fabs(data->y[j] - data->y[j2]); - else if(dim == 2) dist = fabs(data->x[j] - data->x[j2]); + if(dim == 1) + dist = fabs(data->y[j] - data->y[j2]); + else + dist = fabs(data->x[j] - data->x[j2]); if(dist < eps) { hit = TRUE; goto hit2d; } } + hit2d: if(hit) { tothits++; if(indxper[j] == j) indxper[j2] = j; else if(indxper[indxper[j]]==indxper[j]) { -#if 0 - printf("case2: j=[%d %d] i=[%d %d]\n",j,j2,i,i2); -#endif indxper[j2] = indxper[j]; } else { @@ -6607,9 +7798,8 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) } } } - - dx = dy = dz = 0.0; - if(data->dim == 3) { + else if(data->dim == 3) { + dx = dy = dz = 0.0; for(i=1;i<=botn;i++) { j = revindbot[i]; hit = FALSE; @@ -6625,7 +7815,7 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) dx = data->x[j] - data->x[j2]; dz = data->z[j] - data->z[j2]; } - else if(dim == 3) { + else { dx = data->x[j] - data->x[j2]; dy = data->y[j] - data->y[j2]; } @@ -6635,46 +7825,207 @@ int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info) } } - hit3d: - if(hit) { - tothits++; + hit3d: + if(hit) { + tothits++; + indxper[j2] = indxper[j]; + } + else { + printf("The periodic counterpart for node %d was not found!\n",j); + } + } + } + + free_Rvector(toparr,1,topn); + free_Rvector(botarr,1,botn); + free_Ivector(revindtop,1,topn); + free_Ivector(revindbot,1,botn); + } + + if(info) printf("Found all in all %d periodic nodes.\n",tothits); + + free_Ivector(topbot,1,noknots); + + return(0); +} + + + + +int FindPeriodicParents(struct FemType *data,struct BoundaryType *bound,int info) +{ + int i,j,k,k2,l,l2,totsides,newsides,sidenodes,sideelemtype,side; + int noknots,maxhits,nodes,hits,hits2,targets,mappings,targetnode; + int parent,parent2,sideind[MAXNODESD1],sideind2[MAXNODESD1]; + int **periodicparents=NULL, *periodichits=NULL,*periodictarget=NULL,*indexper=NULL; + + totsides = 0; + newsides = 0; + targets = 0; + parent2 = 0; + + if(info) printf("Finding secondary periodic parents for boundary elements\n"); + + if(!data->periodicexist) { + printf("FindPeriodicParents: Periodic nodes are not defined\n"); + return(2); + } + + indexper = data->periodic; + + /* Set pointers that point to the periodic nodes */ + noknots = data->noknots; + periodictarget = Ivector(1,noknots); + for(i=1;i<=noknots;i++) + periodictarget[i] = 0; + + mappings = 0; + for(i=1;i<=noknots;i++) { + j = indexper[i]; + if( j != i) { + mappings++; + periodictarget[j] = i; + } + } + + if(0) for(i=1;i<=noknots;i++) + printf("indexes(%d) : %d %d\n",i,indexper[i],periodictarget[i]); + + + if(info) printf("Number of potential periodic mappings is %d\n",mappings); + for(i=1;i<=noknots;i++) + if(periodictarget[i]) targets++; + if(info) printf("Number of potential periodic targets is %d\n",targets); + + + /* Vector telling how many elements are associated with the periodic nodes */ + maxhits = 0; + periodichits = Ivector(1,noknots); + for(i=1;i<=noknots;i++) + periodichits[i] = 0; + + /* Create the matrix telling which elements are associated with the periodic nodes */ + setparents: + for(j=1;j <= data->noelements;j++) { + nodes = data->elementtypes[j] % 100; + for(i=0;itopology[j][i]; + if( k != indexper[k] ) { + periodichits[k] += 1; + if( maxhits > 0 ) { + periodicparents[k][periodichits[k]] = j; + } + } + } + } + + if( maxhits == 0 ) { + for(i=1;i<=noknots;i++) + maxhits = MAX( maxhits, periodichits[i] ); + + printf("Maximum number of elements associated with periodic nodes is %d\n",maxhits); + periodicparents = Imatrix(1,noknots,1,maxhits); + for(i=1;i<=noknots;i++) { + periodichits[i] = 0; + for(j=1;j<=maxhits;j++) + periodicparents[i][j] = 0; + } + goto setparents; + } + + for(j=0;jelementtypes[parent2]; + elemsides = GetElementFaces(elemtype); + + for(side=0;sidenoknots < 200) { - for(i=1;i<=data->noknots;i++) - if(i!=indxper[i]) printf("i=%d per=%d\n",i,indxper[i]); - } -#endif + free_Ivector(periodictarget,1,noknots); + free_Ivector(periodichits,1,noknots); + free_Imatrix(periodicparents,1,noknots,1,maxhits); + if(info) printf("Found %d secondary parents for %d potential sides.\n",newsides,totsides); return(0); } - int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, int nolayers, int *layerbounds, int *layernumber, Real *layerratios, Real *layerthickness, int *layerparents, @@ -6682,30 +8033,32 @@ int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, /* Create Boundary layers that may be used to solve accurately fluid flow problems and similar equations. */ { - int i,j,k,l,m,n,i2,i3,nonodes,maxbc,newbc = 0; + int i,j,k,l,m,n,i2,i3,nonodes,maxbc,newbc; int noknots,noelements,elemindx,nodeindx,elemtype; int oldnoknots,oldnoelements,maxelemtype,oldmaxnodes; - int nonewnodes,nonewelements,dolayer,dim,order = 0,midpoints = 0; + int nonewnodes,nonewelements,dolayer,dim,order,midpoints; int checkmaterials,parent,parent2,use2,second; - Real dx = 0,dy = 0,ds,ratio,q,p,rectfactor; - Real *newx,*newy,*newz,*oldx,*oldy,*elemwidth; + Real dx,dy,ds,ratio,q,p,rectfactor; + Real *newx=NULL,*newy=NULL,*newz=NULL,*oldx=NULL,*oldy=NULL,*elemwidth=NULL; Real e1x,e1y,e2x,e2y; int sideelemtype,ind[MAXNODESD2],sidebc[MAXNODESD1]; - int *layernode,*newelementtypes,**newtopo,**oldtopo; - int *topomap,*newmaterial,*herit,*inside,*nonlin = NULL; - int endbcs, *endparents = NULL, *endtypes = NULL, *endnodes = NULL, *endnodes2 = NULL, *endneighbours = NULL; + int *layernode=NULL,*newelementtypes=NULL,**newtopo=NULL,**oldtopo=NULL; + int *topomap=NULL,*newmaterial=NULL,*herit=NULL,*inside=NULL,*nonlin=NULL; + int endbcs, *endparents=NULL, *endtypes=NULL, *endnodes=NULL, *endnodes2=NULL, *endneighbours=NULL; - if(maxfilters == 1) maxfilters = 1000; - if(layereps > 0.1) layereps = 0.001; - if(layereps < 1.0e-8) layereps = 0.001; - rectfactor = 1.0e2; + if(0) printf("maxfilters=%d layereps=%.3e\n",maxfilters,layereps); + if(!maxfilters) maxfilters = 1000; + if(layereps < 1.0e-20) layereps = 1.0e-3; + rectfactor = 1.0e2; + midpoints = FALSE; + order = 1; dim = data->dim; maxelemtype = GetMaxElementType(data); if(maxelemtype > 409) { - printf("Subroutine implemented only up to 2nd degree!\n"); - return(2); + printf("Subroutine implemented only up to 2nd degree in 2D!\n"); + bigerror("Cannot continue"); } if(info) printf("Largest elementtype is %d\n",maxelemtype); @@ -7296,9 +8649,9 @@ int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, if(maxfilters) { int method,iter; - int ind1,ind2,ind3,*fixedx,*fixedy; - Real *aidx,*aidy,*weights; - Real maxerror = 0,minds,dx2 = 0,dy2 = 0,ds2,fii; + int ind1,ind2,ind3,*fixedx=NULL,*fixedy=NULL; + Real *aidx=NULL,*aidy=NULL,*weights=NULL; + Real maxerror=0.0,minds,dx2,dy2,ds2,fii; /* There are three methods how to put the weight in the filter, 1) 1/s, 2) fii/s, 3) sin(fii)/s, the second option seems to be best. */ @@ -7511,8 +8864,8 @@ int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, if(j <= oldnoelements && ds * ds2 < 1.0e-50) { printf("problem elem %d and nodes %d (%d %d)\n",j,i2,i,i3); - printf("dist ds=%.3le ds2=%.3le\n",ds,ds2); - printf("coord: %.3le %.3le\n",oldx[oldtopo[j][i2]], oldy[oldtopo[j][i2]]); + printf("dist ds=%.3e ds2=%.3e\n",ds,ds2); + printf("coord: %.3e %.3e\n",oldx[oldtopo[j][i2]], oldy[oldtopo[j][i2]]); continue; } @@ -7602,7 +8955,7 @@ int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, } if(info) { - printf("Filtered the new node coordinates %d times with final error %.3le.\n", + printf("Filtered the new node coordinates %d times with final error %.3e.\n", iter-1,maxerror); } @@ -7734,14 +9087,15 @@ int CreateBoundaryLayerDivide(struct FemType *data,struct BoundaryType *bound, flow problems and similar equations. In this subroutine the boundary layer is created by dividing the elements close to boundary. */ { - int i,j,k,l,dim,maxbc,maxelemtype,dolayer,parent,nlayer = 0,sideelemtype,elemind,side; + int i,j,k,l,dim,maxbc,maxelemtype,dolayer,parent,nlayer,sideelemtype,elemind,side; int noelements,noknots,oldnoknots,oldnoelements,oldmaxnodes,nonewnodes,nonewelements; int maxcon,elemsides,elemdone,midpoints,order,bcnodes,elemhits,elemtype,goforit; int ind[MAXNODESD2],baseind[2],topnode[2],basenode[2]; - int *layernode,*newelementtypes,**newtopo,**oldtopo,*newmaterial,**edgepairs,*sharednode; + int *layernode=NULL,*newelementtypes=NULL,**newtopo=NULL,**oldtopo=NULL; + int *newmaterial=NULL,**edgepairs=NULL,*sharednode=NULL; Real dx[2],dy[2],x0[2],y0[2]; - Real *newx,*newy,*newz,*oldx,*oldy,*oldz; - Real slayer = 0,qlayer = 0,ratio,q; + Real *newx=NULL,*newy=NULL,*newz=NULL,*oldx=NULL,*oldy=NULL,*oldz=NULL; + Real slayer,qlayer,ratio,q; dim = data->dim; @@ -7772,6 +9126,9 @@ int CreateBoundaryLayerDivide(struct FemType *data,struct BoundaryType *bound, the numbder of nodes at the surface. */ maxbc = 0; + qlayer = 0.0; + slayer = 0.0; + nlayer = 0; /* Go through the layers and check which ones are active */ for(j=0;jscale) { if(info) printf("Scaling mesh with vector [%.3lg %.3lg %.3lg]\n", @@ -8205,7 +9562,7 @@ int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info) for(i=1;i<=data->noknots;i++) { data->x[i] *= eg->cscale[0]; data->y[i] *= eg->cscale[1]; - if(data->dim == 3) data->z[i] *= eg->cscale[2]; + data->z[i] *= eg->cscale[2]; } if(0) printf("Scaling of mesh finished.\n"); } @@ -8220,15 +9577,13 @@ int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info) for(i=1;i<=data->noknots;i++) { x = data->x[i]; - if(data->dim >= 2) y = data->y[i]; - else y = 0.0; - if(data->dim >= 3) z = data->z[i]; - else z = 0.0; + y = data->y[i]; + z = data->z[i]; xz = x*cos(cz) + y*sin(cz); yz = -x*sin(cz) + y*cos(cz); - if(data->dim == 3) { + if( fabs(cx) > 1.0e-8 || fabs(cy) > 1.0e-8 ) { yx = yz*cos(cx) + z*sin(cx); zx = -yz*sin(cx) + z*cos(cx); @@ -8253,7 +9608,7 @@ int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info) for(i=1;i<=data->noknots;i++) { data->x[i] += eg->ctranslate[0]; data->y[i] += eg->ctranslate[1]; - if(data->dim == 3) data->z[i] += eg->ctranslate[2]; + data->z[i] += eg->ctranslate[2]; } if(0) printf("Translation of mesh finished.\n"); } @@ -8261,28 +9616,26 @@ int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info) if(eg->center) { xmin = xmax = data->x[1]; ymin = ymax = data->y[1]; - if(data->dim == 3) zmin = zmax = data->z[1]; + zmin = zmax = data->z[1]; for(i=1;i<=data->noknots;i++) { xmax = MAX( xmax, data->x[i] ); xmin = MIN( xmin, data->x[i] ); ymax = MAX( ymax, data->y[i] ); ymin = MIN( ymin, data->y[i] ); - if(data->dim == 3) { - zmax = MAX( zmax, data->z[i] ); - zmin = MIN( zmin, data->z[i] ); - } + zmax = MAX( zmax, data->z[i] ); + zmin = MIN( zmin, data->z[i] ); } cx = 0.5 * (xmin + xmax); cy = 0.5 * (ymin + ymax); - if(data->dim == 3) cz = 0.5 * (zmin + zmax); - - if(info) printf("Setting new center to %.3le %.3le %.3le\n",cx,cy,cz); + cz = 0.5 * (zmin + zmax); + + if(info) printf("Setting new center to %.3e %.3e %.3e\n",cx,cy,cz); for(i=1;i<=data->noknots;i++) { data->x[i] -= cx; data->y[i] -= cy; - if(data->dim == 3) data->z[i] -= cz; + data->z[i] -= cz; } } @@ -8291,15 +9644,17 @@ int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info) -int CreateDualGraph(struct FemType *data,int full,int info) +int CreateNodalGraph(struct FemType *data,int full,int info) { int i,j,k,l,m,totcon,noelements, noknots,elemtype,nonodes,hit,ind,ind2; int maxcon,percon,edge; - printf("Creating a dual graph of the finite element mesh\n"); + printf("Creating a nodal graph of the finite element mesh\n"); - if(data->dualexists) { - printf("The dual graph already exists! You should remove the old graph!\n"); + if(data->nodalexists) { + printf("The nodal graph already exists!\n"); + smallerror("Nodal graph not done"); + return(1); } maxcon = 0; @@ -8311,6 +9666,7 @@ int CreateDualGraph(struct FemType *data,int full,int info) for(i=1;i<=noelements;i++) { elemtype = data->elementtypes[i]; + /* This sets only the connections resulting from element edges */ if(!full) { int inds[2]; for(edge=0;;edge++) { @@ -8321,38 +9677,39 @@ int CreateDualGraph(struct FemType *data,int full,int info) hit = FALSE; for(l=0;ldualgraph[l][ind] == ind2) hit = TRUE; - if(data->dualgraph[l][ind] == 0) break; + if(data->nodalgraph[l][ind] == ind2) hit = TRUE; + if(data->nodalgraph[l][ind] == 0) break; } if(!hit) { if(l >= maxcon) { - data->dualgraph[maxcon] = Ivector(1,noknots); + data->nodalgraph[maxcon] = Ivector(1,noknots); for(m=1;m<=noknots;m++) - data->dualgraph[maxcon][m] = 0; + data->nodalgraph[maxcon][m] = 0; maxcon++; } - data->dualgraph[l][ind] = ind2; + data->nodalgraph[l][ind] = ind2; totcon++; } /* Make also so symmetric connection */ for(l=0;ldualgraph[l][ind2] == ind) hit = TRUE; - if(data->dualgraph[l][ind2] == 0) break; + if(data->nodalgraph[l][ind2] == ind) hit = TRUE; + if(data->nodalgraph[l][ind2] == 0) break; } if(!hit) { if(l >= maxcon) { - data->dualgraph[maxcon] = Ivector(1,noknots); + data->nodalgraph[maxcon] = Ivector(1,noknots); for(m=1;m<=noknots;m++) - data->dualgraph[maxcon][m] = 0; + data->nodalgraph[maxcon][m] = 0; maxcon++; } - data->dualgraph[l][ind2] = ind; + data->nodalgraph[l][ind2] = ind; totcon++; } } } + /* This sets all elemental connections */ else { nonodes = data->elementtypes[i] % 100; for(j=0;jdualgraph[l][ind] == ind2) hit = TRUE; - if(data->dualgraph[l][ind] == 0) break; + if(data->nodalgraph[l][ind] == ind2) hit = TRUE; + if(data->nodalgraph[l][ind] == 0) break; } if(!hit) { if(l >= maxcon) { - data->dualgraph[maxcon] = Ivector(1,noknots); + data->nodalgraph[maxcon] = Ivector(1,noknots); for(m=1;m<=noknots;m++) - data->dualgraph[maxcon][m] = 0; + data->nodalgraph[maxcon][m] = 0; maxcon++; } - data->dualgraph[l][ind] = ind2; + data->nodalgraph[l][ind] = ind2; totcon++; } } @@ -8382,6 +9739,7 @@ int CreateDualGraph(struct FemType *data,int full,int info) } + /* This adds the periodic connections */ if( data->periodicexist ) { for(ind=1;ind<=noknots;ind++) { ind2 = data->periodic[ind]; @@ -8389,53 +9747,55 @@ int CreateDualGraph(struct FemType *data,int full,int info) hit = FALSE; for(l=0;ldualgraph[l][ind] == ind2) hit = TRUE; - if(data->dualgraph[l][ind] == 0) break; + if(data->nodalgraph[l][ind] == ind2) hit = TRUE; + if(data->nodalgraph[l][ind] == 0) break; } if(!hit) { if(l >= maxcon) { - data->dualgraph[maxcon] = Ivector(1,noknots); + data->nodalgraph[maxcon] = Ivector(1,noknots); for(m=1;m<=noknots;m++) - data->dualgraph[maxcon][m] = 0; + data->nodalgraph[maxcon][m] = 0; maxcon++; } - data->dualgraph[l][ind] = ind2; + data->nodalgraph[l][ind] = ind2; totcon++; percon++; } } } - data->dualmaxconnections = maxcon; - data->dualexists = TRUE; + data->nodalmaxconnections = maxcon; + data->nodalexists = TRUE; - if(info) printf("There are at maximum %d connections in dual graph.\n",maxcon); - if(info) printf("There are at all in all %d connections in dual graph.\n",totcon); - if(info && percon) printf("There are %d periodic connections in dual graph.\n",percon); + if(info) { + printf("There are at maximum %d connections in nodal graph.\n",maxcon); + printf("There are at all in all %d connections in nodal graph.\n",totcon); + if(percon) printf("There are %d periodic connections in nodal graph.\n",percon); + } return(0); } -int DestroyDualGraph(struct FemType *data,int info) +int DestroyNodalGraph(struct FemType *data,int info) { int i,maxcon, noknots; - if(!data->dualexists) { - printf("You tried to destroy a non-existing dual graph\n"); + if(!data->nodalexists) { + printf("You tried to destroy a non-existing nodal graph\n"); return(1); } - maxcon = data->dualmaxconnections; + maxcon = data->nodalmaxconnections; noknots = data->noknots; for(i=0;idualgraph[i],1,noknots); + free_Ivector(data->nodalgraph[i],1,noknots); - data->dualmaxconnections = 0; - data->dualexists = FALSE; + data->nodalmaxconnections = 0; + data->nodalexists = FALSE; - if(info) printf("The dual graph was destroyed\n"); + if(info) printf("The nodal graph was destroyed\n"); return(0); } @@ -8443,162 +9803,341 @@ int DestroyDualGraph(struct FemType *data,int info) int CreateInverseTopology(struct FemType *data,int info) { - int i,j,l,m,noelements,noknots,elemtype,nonodes,ind,maxcon; + int i,j,k,l,m,noelements,noknots,elemtype,nonodes,ind; int *neededby,minneeded,maxneeded; + int step,totcon; + int *rows,*cols; + struct CRSType *invtopo; - printf("Creating an inverse topology of the finite element mesh\n"); - - if(data->invtopoexists) { - printf("The inverse topology already exists!\n"); - smallerror("The inverse topology not done"); + invtopo = &data->invtopo; + if(invtopo->created) { + if(0) printf("The inverse topology already exists!\n"); + return(0); } - maxcon = 0; + printf("Creating an inverse topology of the finite element mesh\n"); + noelements = data->noelements; noknots = data->noknots; neededby = Ivector(1,noknots); - for(i=1;i<=noknots;i++) - neededby[i] = 0; + totcon = 0; - for(i=1;i<=noelements;i++) { - elemtype = data->elementtypes[i]; - nonodes = data->elementtypes[i] % 100; + for(step=1;step<=2;step++) { - for(j=0;jtopology[i][j]; + for(i=1;i<=noknots;i++) + neededby[i] = 0; - neededby[ind] += 1; - l = neededby[ind]; + for(i=1;i<=noelements;i++) { + elemtype = data->elementtypes[i]; + nonodes = data->elementtypes[i] % 100; + + for(j=0;jtopology[i][j]; - if(l > maxcon) { - maxcon++; - data->invtopo[maxcon] = Ivector(1,noknots); - for(m=1;m<=noknots;m++) - data->invtopo[maxcon][m] = 0; + if( step == 1 ) { + neededby[ind] += 1; + totcon += 1; + } + else { + k = rows[ind-1] + neededby[ind]; + cols[k] = i-1; + neededby[ind] += 1; + } } - data->invtopo[l][ind] = i; } - } - + + if( step == 1 ) { + rows = Ivector( 0, noknots ); + rows[0] = 0; + for(i=1;i<=noknots;i++) + rows[i] = rows[i-1] + neededby[i]; + + cols = Ivector( 0, totcon-1 ); + for(i=0;icols = cols; + invtopo->rows = rows; + invtopo->colsize = totcon; + invtopo->rowsize = noknots; + invtopo->created = TRUE; + } + } + minneeded = maxneeded = neededby[1]; for(i=1;i<=noknots;i++) { minneeded = MIN( minneeded, neededby[i]); maxneeded = MAX( maxneeded, neededby[i]); } + free_Ivector(neededby,1,noknots); - if(info) printf("There are from %d to %d connections in the inverse topology.\n",minneeded,maxneeded); - data->invtopoexists = TRUE; - data->maxinvtopo = maxcon; + if(info) { + printf("There are from %d to %d connections in the inverse topology.\n",minneeded,maxneeded); + printf("Each node is in average in %.3f elements\n",1.0*totcon/noknots); + } return(0); } -int MeshTypeStatistics(struct FemType *data,int info) +int CreateDualGraph(struct FemType *data,int unconnected,int info) { - int i,elemtype,maxelemtype,minelemtype,*elemtypes; + int totcon,dcon,noelements,noknots,elemtype,nonodes,i,j,k,l,i2,m,ind,hit,ci,ci2; + int dualmaxcon,invmaxcon,showgraph,freeelements,step,orphanelements; + int *elemconnect,*neededby; + int *dualrow,*dualcol,dualsize,dualmaxelem,allocated; + int *invrow,*invcol; + struct CRSType *dualgraph; - maxelemtype = minelemtype = data->elementtypes[1]; + printf("Creating a dual graph of the finite element mesh\n"); - for(i=1;i<=data->noelements;i++) { - elemtype = data->elementtypes[i]; - maxelemtype = MAX( maxelemtype, elemtype ); - minelemtype = MIN( minelemtype, elemtype ); + dualgraph = &data->dualgraph; + if(dualgraph->created) { + printf("The dual graph already exists!\n"); + return(1); } - elemtypes = Ivector(minelemtype,maxelemtype); - for(i=minelemtype;i<=maxelemtype;i++) - elemtypes[i] = 0; + CreateInverseTopology(data,info); - for(i=1;i<=data->noelements;i++) { - elemtype = data->elementtypes[i]; - elemtypes[elemtype] += 1; + noelements = data->noelements; + noknots = data->noknots; + freeelements = noelements; + orphanelements = 0; + + /* If a dual graph only for the unconnected nodes is requested do that. + Basically the connected nodes are omitted in the graph. */ + if( unconnected ) { + printf("Removing connected nodes from the dual graph\n"); + if( data->nodeconnectexist ) { + if(info) printf("Creating connected elements list from the connected nodes\n"); + SetConnectedElements(data,info); + } + if( data->elemconnectexist ) { + elemconnect = data->elemconnect; + freeelements -= data->elemconnectexist; + } + else { + unconnected = FALSE; + } + if(info) printf("List of unconnected elements created\n"); } - if(info) { - printf("Number of different elementtypes\n"); - for(i=minelemtype;i<=maxelemtype;i++) - if(elemtypes[i]) printf("\t%d\t%d\n",i,elemtypes[i]); - } + showgraph = FALSE; + if(showgraph) printf("elemental graph ij pairs\n"); - free_Ivector(elemtypes,minelemtype,maxelemtype); - return(0); -} + data->dualexists = TRUE; + dualmaxcon = 0; + dualmaxelem = 0; + + invrow = data->invtopo.rows; + invcol = data->invtopo.cols; + /* This marker is used to identify the connections already accounted for */ + neededby = Ivector(1,freeelements); + for(i=1;i<=freeelements;i++) + neededby[i] = 0; + + allocated = FALSE; + omstart: + totcon = 0; -int SideAndBulkMappings(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info) -{ - int i,j,l,currenttype; - + for(i=1;i<=noelements;i++) { + if(showgraph) printf("%d :: ",i); - if(eg->sidemappings) { - for(l=0;lsidemappings;l++) - if(info) printf("Setting boundary types between %d and %d to %d\n", - eg->sidemap[3*l],eg->sidemap[3*l+1],eg->sidemap[3*l+2]); + dcon = 0; + elemtype = data->elementtypes[i]; + nonodes = data->elementtypes[i] % 100; - for(j=0;j < MAXBOUNDARIES;j++) { - if(!bound[j].created) continue; + if( unconnected ) { + ci = elemconnect[i]; + if( ci < 0 ) continue; + } + else { + ci = i; + } + if(allocated) dualrow[ci-1] = totcon; + + if(0) printf("i=%d %d\n",i,elemtype); + + for(step=1;step<=2;step++) { + for(j=0;jtopology[i][j]; - for(i=1; i <= bound[j].nosides; i++) { - if(currenttype = bound[j].types[i]) { - for(l=0;lsidemappings;l++) { - if(currenttype >= eg->sidemap[3*l] && currenttype <= eg->sidemap[3*l+1]) { - bound[j].types[i] = eg->sidemap[3*l+2]; - currenttype = -1; - } + if(0) printf("ind=%d\n",ind); + + + for(k=invrow[ind-1];k dualmaxcon ) { + dualmaxcon = dcon; + dualmaxelem = i; } } - } - } - if(info) printf("Renumbering boundary types finished\n"); - } - - if(eg->bulkmappings) { - for(l=0;lbulkmappings;l++) - if(info) printf("Setting material types between %d and %d to %d\n", - eg->bulkmap[3*l],eg->bulkmap[3*l+1],eg->bulkmap[3*l+2]); - for(j=1;j<=data->noelements;j++) { - currenttype = data->material[j]; - for(l=0;lbulkmappings;l++) { - if(currenttype >= eg->bulkmap[3*l] && currenttype <= eg->bulkmap[3*l+1]) { - data->material[j] = eg->bulkmap[3*l+2]; - currenttype = -1; + else { + neededby[ci2] = FALSE; + } } } } - if(info) printf("Renumbering material indexes finished\n"); + if( dcon == 0 && allocated ) { + orphanelements += 1; + } + } + + if(allocated) { + dualrow[dualsize] = totcon; + } + else { + dualsize = freeelements; + dualrow = Ivector(0,dualsize); + for(i=1;i<=dualsize;i++) + dualrow[i] = 0; + + dualcol = Ivector(0,totcon-1); + for(i=0;icols = dualcol; + dualgraph->rows = dualrow; + dualgraph->rowsize = dualsize; + dualgraph->colsize = totcon; + dualgraph->created = TRUE; + + allocated = TRUE; + + goto omstart; + } + + if( orphanelements ) { + printf("There are %d elements in the dual mesh that are not connected!\n",orphanelements); + if(unconnected) printf("The orphan elements are likely caused by the hybrid partitioning\n"); + } + + +#if 0 + j = totcon; k = 0; + for(i=1;i<=dualsize;i++){ + l = dualrow[i]-dualrow[i-1]; + if(l <= 0 ) printf("row(%d) = %d %d %d\n",i,l,dualrow[i],dualrow[i-1]); + j = MIN(j,l); + k = MAX(k,l); + } + printf("range dualrow: %d %d\n",j,k); + + j = totcon; k = 0; + for(i=0;icreated) { + if(0) printf("You tried to destroy a non-existing sparse matrix\n"); + return(1); } + + free_Ivector( sp->rows, 0, sp->rowsize ); + free_Ivector( sp->cols, 0, sp->colsize-1); + sp->rowsize = 0; + sp->colsize = 0; + sp->created = FALSE; + return(0); } +int DestroyInverseTopology(struct FemType *data,int info) +{ + DestroyCRSMatrix( &data->invtopo ); +} -int SideAndBulkBoundaries(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info) +int DestroyDualGraph(struct FemType *data,int info) { - int l; - int *boundnodes,noboundnodes; - boundnodes = Ivector(1,data->noknots); - - if(eg->bulkbounds) { - for(l=0;lbulkbounds;l++) { - FindBulkBoundary(data,eg->bulkbound[3*l],eg->bulkbound[3*l+1], - boundnodes,&noboundnodes,info); - FindNewBoundaries(data,bound,boundnodes,eg->bulkbound[3*l+2],1,info); - } + DestroyCRSMatrix( &data->dualgraph ); +} + + + + +int MeshTypeStatistics(struct FemType *data,int info) +{ + int i,elemtype,maxelemtype,minelemtype; + int *elemtypes=NULL; + + maxelemtype = minelemtype = data->elementtypes[1]; + + for(i=1;i<=data->noelements;i++) { + elemtype = data->elementtypes[i]; + maxelemtype = MAX( maxelemtype, elemtype ); + minelemtype = MIN( minelemtype, elemtype ); } - if(eg->boundbounds) { - for(l=0;lboundbounds;l++) { - FindBoundaryBoundary(data,bound,eg->boundbound[3*l],eg->boundbound[3*l+1], - boundnodes,&noboundnodes,info); - FindNewBoundaries(data,bound,boundnodes,eg->boundbound[3*l+2],2,info); - } + + elemtypes = Ivector(minelemtype,maxelemtype); + for(i=minelemtype;i<=maxelemtype;i++) + elemtypes[i] = 0; + + for(i=1;i<=data->noelements;i++) { + elemtype = data->elementtypes[i]; + elemtypes[elemtype] += 1; } - free_Ivector(boundnodes,1,data->noknots); - return 0; // added by ML 19.03.2008 + if(info) { + printf("Number of different elementtypes\n"); + for(i=minelemtype;i<=maxelemtype;i++) + if(elemtypes[i]) printf("\t%d\t%d\n",i,elemtypes[i]); + } + + free_Ivector(elemtypes,minelemtype,maxelemtype); + return(0); } diff --git a/ElmerGUI/Application/plugins/egmesh.h b/ElmerGUI/Application/plugins/egmesh.h index 16b6b8e549..d1e1b85ee0 100644 --- a/ElmerGUI/Application/plugins/egmesh.h +++ b/ElmerGUI/Application/plugins/egmesh.h @@ -1,100 +1,110 @@ -/* femknot.h */ -/* This module includes utilities that operate on single knots. It builds - structures where the knots can be saved, it finds boundaries, - copies knots from structures to others and destroys structures that - become obsolete. The routines mostly operate on structures - FemType and BoundaryType. */ - -int GetElementDimension(int elementtype); -int GetMaxElementType(struct FemType *data); -int GetMinElementType(struct FemType *data); -int GetMaxElementDimension(struct FemType *data); -void GetElementInfo(int element,struct FemType *data, - Real *globalcoord,int *ind,int *material); -void GetElementSide(int element,int side,int normal, - struct FemType *data,int *ind,int *sideelemtype); -void NumberVariables(struct FemType *data,int variable); -int CalculateIndexwidth(struct FemType *data,int indxis,int *indx); - -void InitializeKnots(struct FemType *data); -void AllocateKnots(struct FemType *data); -void CreateKnots(struct GridType *grid,struct CellType *cell, - struct FemType *data,int noknots,int info); - -int CreateVariable(struct FemType *data,int variable,int unknowns, - Real value,const char *variablename,int eorder); -void DestroyKnots(struct FemType *data); -int FindParentSide(struct FemType *data,struct BoundaryType *bound, - int sideelem,int sideelemtype,int *sideind); -int CreateBoundary(struct CellType *cell,struct FemType *data, - struct BoundaryType *bound,int material1,int material2, - int solidmat,int boundarytype,int info); -int CreateAllBoundaries(struct CellType *cell,struct FemType *data, - struct BoundaryType *bound,int info); -int AllocateBoundary(struct BoundaryType *bound,int size); -int DestroyBoundary(struct BoundaryType *bound); -int CreatePoints(struct CellType *cell,struct FemType *data, - struct BoundaryType *bound, - int param1,int param2,int pointmode,int pointtype,int info); -int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, - int boundtype,int endnodes,int info); -int SetConnectedBoundary(struct FemType *data,struct BoundaryType *bound, - int bctype,int connecttype,int info); -int FindCorners(struct GridType *grid,struct CellType *cell, - struct FemType *data,int info); - -int ConstantToBilinear(struct FemType *data,int var1,int var2); -int ElementsToTriangles(struct FemType *data,struct BoundaryType *bound, - Real critangle,int info); -int IncreaseElementOrder(struct FemType *data,int info); -int PolarCoordinates(struct FemType *data,Real rad,int info); -int CylinderCoordinates(struct FemType *data,int info); -int UniteMeshes(struct FemType *data1,struct FemType *data2, - struct BoundaryType *bound1,struct BoundaryType *bound2, - int info); -int CloneMeshes(struct FemType *data,struct BoundaryType *bound, - int *ncopies,Real *meshsize,int diffmats,int info); -int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, - int *symmaxis,int diffmats,Real *meshsize,int symmbound,int info); -void ReorderElements(struct FemType *data,struct BoundaryType *bound, - int manual,Real corder[],int info); -int RemoveUnusedNodes(struct FemType *data,int info); -void RenumberBoundaryTypes(struct FemType *data,struct BoundaryType *bound, - int renumber, int bcoffset, int info); -void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int info); -void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, - struct GridType *grid, - struct FemType *data,struct BoundaryType *bound, - int info); -void ReduceElementOrder(struct FemType *data,int matmin,int matmax); -void IsoparametricElements(struct FemType *data,struct BoundaryType *bound, - int bcstoo,int info); -void MergeElements(struct FemType *data,struct BoundaryType *bound, - int manual,Real corder[],Real eps,int mergebounds,int info); -void MergeBoundaries(struct FemType *data,struct BoundaryType *bound,int *doubles,int info); -void SeparateCartesianBoundaries(struct FemType *data,struct BoundaryType *bound,int info); -void ElementsToBoundaryConditions(struct FemType *data, - struct BoundaryType *bound,int retainorphans,int info); -int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info); -int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, - int *boundnodes,int suggesttype,int dimred,int info); -int FindBulkBoundary(struct FemType *data,int mat1,int mat2, - int *boundnodes,int *noboundnodes,int info); -int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat1,int mat2, - int *boundnodes,int *noboundnodes,int info); -int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, - int nolayers, int *layerbounds, int *layernumber, - Real *layerratios, Real *layerthickness, int *layerparents, - int maxfilters, Real layereps, int info); -int CreateBoundaryLayerDivide(struct FemType *data,struct BoundaryType *bound, - int nolayers, int *layerbounds, int *layernumber, - Real *layerratios, Real *layerthickness, int *layerparents,int info); -int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info); -int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *bound,int info); - -int CreateDualGraph(struct FemType *data,int full,int info); -int DestroyDualGraph(struct FemType *data,int info); -int CreateInverseTopology(struct FemType *data,int info); -int MeshTypeStatistics(struct FemType *data,int info); -int SideAndBulkMappings(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info); -int SideAndBulkBoundaries(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info); +/* femknot.h -> egmesh.h */ +/* This module includes utilities that operate on single knots. It builds + structures where the knots can be saved, it finds boundaries, + copies knots from structures to others and destroys structures that + become obsolete. The routines mostly operate on structures + FemType and BoundaryType. */ + +int GetElementDimension(int elementtype); +int GetMaxElementType(struct FemType *data); +int GetMinElementType(struct FemType *data); +int GetMaxElementDimension(struct FemType *data); +int GetCoordinateDimension(struct FemType *data,int info); +void GetElementInfo(int element,struct FemType *data, + Real *globalcoord,int *ind,int *material); +void GetBoundaryElement(int sideind,struct BoundaryType *bound,struct FemType *data,int *ind,int *sideelemtype); +void GetElementSide(int element,int side,int normal, + struct FemType *data,int *ind,int *sideelemtype); +int GetElementFaces(int elemtype); +void NumberVariables(struct FemType *data,int variable); +int CalculateIndexwidth(struct FemType *data,int indxis,int *indx); +void InitializeKnots(struct FemType *data); +void AllocateKnots(struct FemType *data); +void CreateKnots(struct GridType *grid,struct CellType *cell, + struct FemType *data,int noknots,int info); +int CreateVariable(struct FemType *data,int variable,int unknowns, + Real value,const char *variablename,int eorder); +void DestroyKnots(struct FemType *data); +int CreateBoundary(struct CellType *cell,struct FemType *data, + struct BoundaryType *bound,int material1,int material2, + int solidmat,int boundarytype,int info); +int AllocateBoundary(struct BoundaryType *bound,int size); +int DestroyBoundary(struct BoundaryType *bound); +int CreateBoundaries(struct CellType *cell,struct FemType *data, + struct BoundaryType *boundaries,int info); +int CreatePoints(struct CellType *cell,struct FemType *data, + struct BoundaryType *bound, + int param1,int param2,int pointmode,int pointtype,int info); +int CreateNewNodes(struct FemType *data,int *order,int material,int newknots); +int SetDiscontinuousBoundary(struct FemType *data,struct BoundaryType *bound, + int boundtype,int endnodes,int info); +int SetConnectedNodes(struct FemType *data,struct BoundaryType *bound, + int bctype,int connecttype,int info); +int SetConnectedElements(struct FemType *data,int info); +int FindCorners(struct GridType *grid,struct CellType *cell, + struct FemType *data,int info); + +int ConstantToBilinear(struct FemType *data,int var1,int var2); +int ElementsToTriangles(struct FemType *data,struct BoundaryType *bound, + Real critangle,int info); +int IncreaseElementOrder(struct FemType *data,int info); +int PolarCoordinates(struct FemType *data,Real rad,int info); +int CylinderCoordinates(struct FemType *data,int info); +int UniteMeshes(struct FemType *data1,struct FemType *data2, + struct BoundaryType *bound1,struct BoundaryType *bound2, + int nooverlap, int info); +int CloneMeshes(struct FemType *data,struct BoundaryType *bound, + int *ncopies,Real *meshsize,int diffmats,int info); +int MirrorMeshes(struct FemType *data,struct BoundaryType *bound, + int *symmaxis,int diffmats,Real *meshsize,int symmbound,int info); +void ReorderElements(struct FemType *data,struct BoundaryType *bound, + int manual,Real corder[],int info); +int RemoveUnusedNodes(struct FemType *data,int info); +void RenumberBoundaryTypes(struct FemType *data,struct BoundaryType *bound, + int renumber, int bcoffset, int info); +void RenumberMaterialTypes(struct FemType *data,struct BoundaryType *bound,int info); +void CreateKnotsExtruded(struct FemType *dataxy,struct BoundaryType *boundxy, + struct GridType *grid, + struct FemType *data,struct BoundaryType *bound, + int info); +void CylindricalCoordinateCurve(struct FemType *data, + Real zet,Real rad,Real angle); +void ReduceElementOrder(struct FemType *data,int matmin,int matmax); +void IsoparametricElements(struct FemType *data,struct BoundaryType *bound, + int bcstoo,int info); +void MergeElements(struct FemType *data,struct BoundaryType *bound, + int manual,Real corder[],Real eps,int mergebounds,int info); +void MergeBoundaries(struct FemType *data,struct BoundaryType *bound,int *doubles,int info); +void SeparateCartesianBoundaries(struct FemType *data,struct BoundaryType *bound,int info); +void ElementsToBoundaryConditions(struct FemType *data, + struct BoundaryType *bound,int retainorphans, int info); +int SideAndBulkMappings(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info); +int SideAndBulkBoundaries(struct FemType *data,struct BoundaryType *bound,struct ElmergridType *eg,int info); +void NodesToBoundaryChain(struct FemType *data,struct BoundaryType *bound, + int *bcinds,int *bctags,int nbc,int bccount, + int info); +int FindPeriodicNodes(struct FemType *data,int periodicdim[],int info); +int FindPeriodicParents(struct FemType *data,struct BoundaryType *bound,int info); +int FindNewBoundaries(struct FemType *data,struct BoundaryType *bound, + int *boundnodes,int suggesttype,int dimred,int info); +int FindBulkBoundary(struct FemType *data,int mat1,int mat2, + int *boundnodes,int *noboundnodes,int info); +int FindBoundaryBoundary(struct FemType *data,struct BoundaryType *bound,int mat1,int mat2, + int *boundnodes,int *noboundnodes,int info); +int CreateBoundaryLayer(struct FemType *data,struct BoundaryType *bound, + int nolayers, int *layerbounds, int *layernumber, + Real *layerratios, Real *layerthickness, int *layerparents, + int maxfilters, Real layereps, int info); +int CreateBoundaryLayerDivide(struct FemType *data,struct BoundaryType *bound, + int nolayers, int *layerbounds, int *layernumber, + Real *layerratios, Real *layerthickness, int *layerparents,int info); +int RotateTranslateScale(struct FemType *data,struct ElmergridType *eg,int info); +int RemoveLowerDimensionalBoundaries(struct FemType *data,struct BoundaryType *bound,int info); +int RemoveInternalBoundaries(struct FemType *data,struct BoundaryType *bound,int info); +int CreateNodalGraph(struct FemType *data,int full,int info); +int DestroyNodalGraph(struct FemType *data,int info); +int CreateDualGraph(struct FemType *data,int unconnected,int info); +int DestroyDualGraph(struct FemType *data,int info); +int CreateInverseTopology(struct FemType *data,int info); +int DestroyInverseTopology(struct FemType *data,int info); +int MeshTypeStatistics(struct FemType *data,int info); diff --git a/ElmerGUI/Application/plugins/egnative.cpp b/ElmerGUI/Application/plugins/egnative.cpp index e27573b3cf..de340b0ae4 100644 --- a/ElmerGUI/Application/plugins/egnative.cpp +++ b/ElmerGUI/Application/plugins/egnative.cpp @@ -1,4 +1,4 @@ -/* +/* ElmerGrid - A simple mesh generation and manipulation utility Copyright (C) 1995- , CSC - IT Center for Science Ltd. @@ -23,48 +23,309 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/* -------------------------------: egnative.c :---------------------------- + This module includes routines for I/O of native formats of Elmer. +*/ - /* -----------------------: egnative.c :---------------------- - - These subroutines are used to create the native mesh of ElmerGrid. - */ - -#include #include -#include +#include #include +#include #include -#include -#include +#include + +#if HAVE_UNISTD_H +#include +#endif + #include +#include +#include +#include +#include +#include #include "egutils.h" #include "egdef.h" #include "egtypes.h" -#include "egnative.h" #include "egmesh.h" +/* #include "egparallel.h" */ +#include "egnative.h" +/*#include "../config.h"*/ + +#define GETLINE ioptr=fgets(line,MAXLINESIZE,in) +static char *ioptr; + + +#define DEBUG 0 + + +int matcactive=FALSE, iodebug=FALSE; + +#define MAXINMETHODS 21 +const char *InMethods[] = { + /*0*/ "EG", + /*1*/ "ELMERGRID", + /*2*/ "ELMERSOLVER", + /*3*/ "ELMERPOST", + /*4*/ "ANSYS", + /*5*/ "IDEAS", + /*6*/ "ABAQUS", + /*7*/ "FIDAP", + /*8*/ "UNV", + /*9*/ "COMSOL", + /*10*/ "FIELDVIEW", + /*11*/ "TRIANGLE", + /*12*/ "MEDIT", + /*13*/ "GID", + /*14*/ "GMSH", + /*15*/ "PARTITIONED", + /*16*/ "FVCOM", + /*17*/ "NASTRAN", + /*18*/ "CGSIM", + /*19*/ "GEO", + /*20*/ "FLUX2D", + /*21*/ "FLUX3D", +}; + + +#define MAXOUTMETHODS 5 +const char *OutMethods[] = { + /*0*/ "EG", + /*1*/ "ELMERGRID", + /*2*/ "ELMERSOLVER", + /*3*/ "ELMERPOST", + /*4*/ "GMSH", + /*5*/ "VTU", +}; + + +void Instructions() +{ + printf("****************** Elmergrid ************************\n"); + printf("This program can create simple 2D structured meshes consisting of\n"); + printf("linear, quadratic or cubic rectangles or triangles. The meshes may\n"); + printf("also be extruded and revolved to create 3D forms. In addition many\n"); + printf("mesh formats may be imported into Elmer software. Some options have\n"); + printf("not been properly tested. Contact the author if you face problems.\n\n"); + + printf("The program has two operation modes\n"); + printf("A) Command file mode which has the command file as the only argument\n"); + printf(" 'ElmerGrid commandfile.eg'\n\n"); + + printf("B) Inline mode which expects at least three input parameters\n"); + printf(" 'ElmerGrid 1 3 test'\n\n"); + printf("The first parameter defines the input file format:\n"); + printf("1) .grd : ElmerGrid file format\n"); + printf("2) .mesh.* : Elmer input format\n"); + printf("3) .ep : Elmer output format\n"); + printf("4) .ansys : Ansys input format\n"); + printf("5) .inp : Abaqus input format by Ideas\n"); + printf("6) .fil : Abaqus output format\n"); + printf("7) .FDNEUT : Gambit (Fidap) neutral file\n"); + printf("8) .unv : Universal mesh file format\n"); + printf("9) .mphtxt : Comsol Multiphysics mesh format\n"); + printf("10) .dat : Fieldview format\n"); + printf("11) .node,.ele: Triangle 2D mesh format\n"); + printf("12) .mesh : Medit mesh format\n"); + printf("13) .msh : GID mesh format\n"); + printf("14) .msh : Gmsh mesh format\n"); + printf("15) .ep.i : Partitioned ElmerPost format\n"); + printf("16) .2dm : 2D triangular FVCOM format\n"); +#if 0 + printf("17) .msh : Nastran format\n"); + printf("18) .msh : CGsim format\n"); + printf("19) .geo : Geo format\n"); + printf("20) .tra : Cedrat Flux 2D format\n"); + printf("21) .pf3 : Cedrat Flux 3D format\n"); +#endif + + printf("\nThe second parameter defines the output file format:\n"); + printf("1) .grd : ElmerGrid file format\n"); + printf("2) .mesh.* : ElmerSolver format (also partitioned .part format)\n"); + printf("3) .ep : ElmerPost format\n"); + printf("4) .msh : Gmsh mesh format\n"); + printf("5) .vtu : VTK ascii XML format\n"); +#if 0 + printf("5) .inp : Abaqus input format\n"); + printf("7) .fidap : Fidap format\n"); + printf("18) .ep : Fastcap input format.\n"); +#endif + + printf("\nThe third parameter is the name of the input file.\n"); + printf("If the file does not exist, an example with the same name is created.\n"); + printf("The default output file name is the same with a different suffix.\n\n"); + + printf("There are several additional in-line parameters that are\n"); + printf("taken into account only when applicable to the given format.\n"); + + printf("-out str : name of the output file\n"); + printf("-in str : name of a secondary input file\n"); + printf("-decimals : number of decimals in the saved mesh (eg. 8)\n"); + printf("-relh real : give relative mesh density parameter for ElmerGrid meshing\n"); + printf("-triangles : rectangles will be divided to triangles\n"); + printf("-merge real : merges nodes that are close to each other\n"); + printf("-order real[3] : reorder elements and nodes using c1*x+c2*y+c3*z\n"); + printf("-centralize : set the center of the mesh to origin\n"); + printf("-scale real[3] : scale the coordinates with vector real[3]\n"); + printf("-translate real[3] : translate the nodes with vector real[3]\n"); + printf("-rotate real[3] : rotate around the main axis with angles real[3]\n"); + printf("-clone int[3] : make ideantilcal copies of the mesh\n"); + printf("-clonesize real[3] : the size of the mesh to be cloned if larger to the original\n"); + printf("-mirror int[3] : copy the mesh around the origin in coordinate directions\n"); + printf("-cloneinds : when performing cloning should cloned entities be given new indexes\n"); + printf("-unite : the meshes will be united\n"); + printf("-unitenooverlap : the meshes will be united without overlap in entity numbering\n"); + printf("-polar real : map 2D mesh to a cylindrical shell with given radius\n"); + printf("-cylinder : map 2D/3D cylindrical mesh to a cartesian mesh\n"); + printf("-reduce int[2] : reduce element order at material interval [int1 int2]\n"); + printf("-increase : increase element order from linear to quadratic\n"); + printf("-bcoffset int : add an offset to the boundary conditions\n"); + printf("-discont int : make the boundary to have secondary nodes\n"); + printf("-connect int : make the boundary to have internal connection among its elements\n"); + printf("-removeintbcs : remove internal boundaries if they are not needed\n"); + printf("-removelowdim : remove boundaries that are two ranks lower than highest dim\n"); + printf("-removeunused : remove nodes that are not used in any element\n"); + printf("-bulkorder : renumber materials types from 1 so that every number is used\n"); + printf("-boundorder : renumber boundary types from 1 so that every number is used\n"); + printf("-autoclean : this performs the united action of the four above\n"); + printf("-bulkbound int[3] : set the intersection of materials [int1 int2] to be boundary int3\n"); + printf("-boundbound int[3] : set the intersection of boundaries [int1 int2] to be boundary int3\n"); + printf("-bulktype int[3] : set material types in interval [int1 int2] to type int3\n"); + printf("-boundtype int[3] : set sidetypes in interval [int1 int2] to type int3\n"); + printf("-layer int[2] real[2]: make a boundary layer for given boundary\n"); + printf("-layermove int : apply Jacobi filter int times to move the layered mesh\n"); + printf("-divlayer int[2] real[2]: make a boundary layer for given boundary\n"); + printf("-3d / -2d / -1d : mesh is 3, 2 or 1-dimensional (applies to examples)\n"); + printf("-isoparam : ensure that higher order elements are convex\n"); + printf("-nonames : disable use of mesh.names even if it would be supported by the format\n"); + printf("-nosave : disable saving part altogether\n"); + printf("-nooverwrite : if mesh already exists don't overwrite it\n"); + printf("-vtuone : start real node indexes in vtu file from one\n"); + printf("-timer : show timer information\n"); + printf("-infofile str : file for saving the timer and size information\n"); + + printf("\nKeywords are related to mesh partitioning for parallel ElmerSolver runs:\n"); + printf("-partition int[3] : the mesh will be partitioned in cartesian main directions\n"); + printf("-partorder real[3] : in the 'partition' method set the direction of the ordering\n"); + printf("-parttol real : in the 'partition' method set the tolerance for ordering\n"); + printf("-partcell int[3] : the mesh will be partitioned in cells of fixed sizes\n"); + printf("-partcyl int[3] : the mesh will be partitioned in cylindrical main directions\n"); +#if USE_METIS + printf("-metiskway int : mesh will be partitioned with Metis using graph Kway routine\n"); + printf("-metisrec int : mesh will be partitioned with Metis using graph Recursive routine\n"); + printf("-metiscontig : enforce that the metis partitions are contiguous\n"); + printf("-metisseed int : random number generator seed for Metis algorithms\n"); +#endif + printf("-partdual : use the dual graph in partition method (when available)\n"); + printf("-halo : create halo for the partitioning for DG\n"); + printf("-halobc : create halo for the partitioning at boundaries only\n"); + printf("-haloz / -halor : create halo for the the special z- or r-partitioning\n"); + printf("-halogreedy : create halo being greedy over the partition interfaces\n"); + printf("-indirect : create indirect connections (102 elements) in the partitioning\n"); + printf("-periodic int[3] : periodic coordinate directions for parallel & conforming meshes\n"); + printf("-partoptim : apply aggressive optimization to node sharing\n"); + printf("-partnobcoptim : do not apply optimization to bc ownership sharing\n"); + printf("-partbw : minimize the bandwidth of partition-partion couplings\n"); + printf("-parthypre : number the nodes continuously partitionwise\n"); + printf("-partzbc : partition connected BCs separately to partitions in Z-direction\n"); + printf("-partrbc : partition connected BCs separately to partitions in R-direction\n"); +#if USE_METIS + printf("-metisbc : partition connected BCs separately to partitions by Metis\n"); +#endif + printf("-partlayers int : extend boundary partitioning by element layers\n"); + + printf("\nKeywords are related to (nearly obsolete) ElmerPost format:\n"); + printf("-partjoin int : number of ElmerPost partitions in the data to be joined\n"); + printf("-saveinterval int[3] : the first, last and step for fusing parallel data\n"); + printf("-nobound : disable saving of boundary elements in ElmerPost format\n"); + + if(0) printf("-names : conserve name information where applicable\n"); +} + + +void Goodbye() +{ + printf("\nThank you for using Elmergrid!\n"); + printf("Send bug reports and feature wishes to elmeradm@csc.fi\n"); + exit(0); +} -int matcactive; -#if HAVE_MATC + +#if USE_MATC char *mtc_domath(const char *); void mtc_init(FILE * input, FILE * output, FILE * error); #endif +static int Getline(char *line1,FILE *io) +{ + int i,isend; + char line0[MAXLINESIZE],*charend,*matcpntr,*matcpntr0; + + for(i=0;ilayered = FALSE; grid->layeredbc = TRUE; + grid->layerbcoffset = 0; grid->triangles = FALSE; grid->triangleangle = 0.0; grid->partitions = FALSE; grid->wantedelems = 0; + grid->wantedelems3d = 0; + grid->wantednodes3d = 0; grid->limitdx = 0.0; grid->limitdxverify = FALSE; @@ -76,7 +337,7 @@ void InitGrid(struct GridType *grid) grid->minxelems = grid->minyelems = 1; grid->minzelems = 2; grid->firstmaterial = 1; - grid->lastmaterial = MAXMATERIALS; + grid->lastmaterial = MAXBODYID; grid->autoratio = 1; grid->xyratio = 1.0; grid->xzratio = 1.0; @@ -123,10 +384,14 @@ void InitGrid(struct GridType *grid) grid->zdens[i] = 1.0; grid->z[i] = 0.; grid->zfirstmaterial[i] = 1; - grid->zlastmaterial[i] = MAXMATERIALS; + grid->zlastmaterial[i] = MAXBODYID; grid->zmaterial[i] = 0; } + grid->zmaterialmapexists = FALSE; + grid->zhelicityexists = FALSE; + grid->zhelicity = 0.0; + /* Initializes the numbering of the cells. */ for(j=0;j<=MAXCELLS+1;j++) for(i=0;i<=MAXCELLS+1;i++) @@ -967,12 +1232,12 @@ void SetCellData(struct GridType *grid,struct CellType *cell,int info) cell[cnew].neighbour[7] = grid->numbered[j+1][i-1]; } - if(0) printf("%d cells were created.\n",grid->nocells); + if(info) printf("%d cells were created.\n",grid->nocells); } -int SetCellKnots(struct GridType *grid, struct CellType *cell,int info) +static int SetCellKnots(struct GridType *grid, struct CellType *cell,int info) /* Uses given mesh to number the knots present in the cells. The knots are numbered independently of the cells from left to right and up to down. Only the numbers of four knots at the @@ -989,10 +1254,10 @@ int SetCellKnots(struct GridType *grid, struct CellType *cell,int info) { int i,j,level,center; int degree,centernodes,sidenodes,nonodes; - int cnew = 0,cup = 0,cleft = 0,cleftup = 0; + int cnew=0,cup=0,cleft=0,cleftup=0; int elemno,knotno; int maxwidth,width,numbering; - int xcells,ycells,*yelems,*xelems; + int xcells,ycells,*yelems=NULL,*xelems=NULL; numbering = grid->numbering; nonodes = grid->nonodes; @@ -1179,12 +1444,12 @@ int SetCellKnots(struct GridType *grid, struct CellType *cell,int info) grid->noelements = elemno; if(info) { - printf("There are %d knots in %d %d-node elements.\n",knotno,elemno,nonodes); + printf("Numbered %d knots in %d %d-node elements.\n",knotno,elemno,nonodes); if(numbering == NUMBER_XY) - if(0) printf("Numbering order was and max levelwidth %d.\n", + printf("Numbering order was and max levelwidth %d.\n", maxwidth); else if(numbering == NUMBER_YX) - if(0) printf("Numbering order was and max levelwidth %d.\n", + printf("Numbering order was and max levelwidth %d.\n", maxwidth); } @@ -1193,7 +1458,7 @@ int SetCellKnots(struct GridType *grid, struct CellType *cell,int info) -int SetCellKnots1D(struct GridType *grid, struct CellType *cell,int info) +static int SetCellKnots1D(struct GridType *grid, struct CellType *cell,int info) { int i; int degree,nonodes; @@ -1259,6 +1524,26 @@ int SetCellKnots1D(struct GridType *grid, struct CellType *cell,int info) +void CreateCells(struct GridType *grid,struct CellType **cell,int info) +{ + (*cell) = (struct CellType*) + malloc((size_t) (grid->nocells+1)*sizeof(struct CellType)); + + SetCellData(grid,*cell,info); + + if(grid->dimension == 1) + SetCellKnots1D(grid,*cell,info); + else + SetCellKnots(grid,*cell,info); +} + + +void DestroyCells(struct CellType **cell) +{ + free(cell); +} + + int GetKnotIndex(struct CellType *cell,int i,int j) /* Given the cell and knot indices gives the corresponding @@ -1266,7 +1551,7 @@ int GetKnotIndex(struct CellType *cell,int i,int j) range [0..n] and [0..m]. Requires only the structure CellType. */ { - int ind,aid,maxj = 0; + int ind=0,aid=0,maxj=0; if(cell->numbering == NUMBER_1D) { ind = cell->left1st; @@ -1285,6 +1570,10 @@ int GetKnotIndex(struct CellType *cell,int i,int j) aid = j; j = i; i = aid; maxj = cell->xelem; } + else { + maxj = 0; + bigerror("GetKnotIndex: Unknown numbering scheme!"); + } if(j == 0) ind = cell->left1st; @@ -1360,7 +1649,7 @@ int GetElementIndices(struct CellType *cell,int i,int j,int *ind) requires only the structure CellType. */ { - int nonodes,numbering,elemind = 0; + int nonodes,numbering,elemind=0; nonodes = cell->nonodes; numbering = cell->numbering; @@ -1420,7 +1709,7 @@ int GetElementIndices(struct CellType *cell,int i,int j,int *ind) printf("GetElementIndices: not implemented for %d nodes.\n",nonodes); } - else if(numbering == NUMBER_YX) { + else if(numbering == NUMBER_YX) { elemind = cell->elem1st+(j-1) + (i-1)*cell->elemwidth; if(nonodes == 4) return(elemind); @@ -1495,7 +1784,7 @@ int GetElementIndex(struct CellType *cell,int i,int j) requires only the structure CellType. */ { - int elemind = 0; + int elemind=0; if(cell->numbering == NUMBER_XY) elemind = cell->elem1st+(i-1) + (j-1)*cell->elemwidth; @@ -1516,7 +1805,7 @@ int GetElementCoordinates(struct CellType *cell,int i,int j, rectangular. */ { - int k,nonodes,numbering,elemind = 0; + int k,nonodes,numbering,elemind=0; Real xrat,yrat; k = nonodes = cell->nonodes; @@ -1927,53 +2216,12 @@ void SetElementDivisionCylinder(struct GridType *grid,int info) -static int Getline(char *line1,FILE *io) -{ - int i,isend; - char line0[MAXLINESIZE],*charend; - - for(i=0;idimension = 2; - if(strstr(params,"CARTESIAN 1D")) { + if(strstr(line,"CARTES") && strstr(line,"1D")) { grid[k]->coordsystem = COORD_CART1; grid[k]->dimension = 1; } - else if(strstr(params,"CARTESIAN 2D")) + else if(strstr(line,"CARTES") && strstr(line,"2D")) grid[k]->coordsystem = COORD_CART2; - else if(strstr(params,"AXISYMMETRIC")) + else if(strstr(line,"AXIS") && strstr(line,"2D")) grid[k]->coordsystem = COORD_AXIS; - else if(strstr(params,"POLAR")) + else if(strstr(line,"POLAR") && strstr(line,"2D")) grid[k]->coordsystem = COORD_POLAR; - else if(strstr(params,"CARTESIAN 3D")) { + else if(strstr(line,"CARTES") && strstr(line,"3D")) { grid[k]->coordsystem = COORD_CART3; grid[k]->dimension = 3; } - else printf("Unknown coordinate system: %s\n",params); - if(0) printf("Defining the coordinate system (%d-DIM).\n",grid[k]->dimension); - } - - else if(strstr(command,"SUBCELL DIVISIONS")) { + else if(strstr(line,"CYLINDRICAL")) { + grid[k]->coordsystem = COORD_CYL; + grid[k]->dimension = 3; + } + else printf("Unknown coordinate system: %s\n",line); + printf("Defining the coordinate system (%d-DIM).\n",grid[k]->dimension); + + Getline(line,in); + if(grid[k]->dimension == 1) { - sscanf(params,"%d",&(*grid)[k].xcells); + sscanf(line,"%d",&(*grid)[k].xcells); grid[k]->ycells = 1; } - else if(grid[k]->dimension == 2) - sscanf(params,"%d %d",&(*grid)[k].xcells,&(*grid)[k].ycells); - else if(grid[k]->dimension == 3) - sscanf(params,"%d %d %d",&(*grid)[k].xcells,&(*grid)[k].ycells,&(*grid)[k].zcells); - if(grid[k]->xcells >= MAXCELLS || grid[k]->ycells >= MAXCELLS || grid[k]->zcells >= MAXCELLS) { - printf("LoadElmergrid: Too many subcells [%d %d %d] vs. %d:\n", + if(grid[k]->dimension == 2) + sscanf(line,"%d %d",&(*grid)[k].xcells,&(*grid)[k].ycells); + if(grid[k]->dimension == 3) + sscanf(line,"%d %d %d",&(*grid)[k].xcells,&(*grid)[k].ycells,&(*grid)[k].zcells); + if(grid[k]->xcells >= MAXCELLS || grid[k]->ycells >= MAXCELLS || + grid[k]->zcells >= MAXCELLS) { + printf("LoadGrid: Too many subcells [%d %d %d] vs. %d:\n", grid[k]->xcells,grid[k]->ycells,grid[k]->zcells,MAXCELLS); } + + if(grid[k]->dimension == 1) { + printf("Loading [%d] subcell intervals in 1D\n", + grid[k]->xcells); + } + else if(grid[k]->dimension == 2) { + printf("Loading [%d %d] subcell intervals in 2D\n", + grid[k]->xcells,grid[k]->ycells); + } else { + printf("Loading [%d %d %d] subcell intervals in 3D\n", + grid[k]->xcells,grid[k]->ycells,grid[k]->zcells); + } - /* Initialize the default structure with ones */ - for(j=grid[k]->ycells;j>=1;j--) + + for(j=1;j<=grid[k]->dimension;j++) { + Getline(line,in); + cp=line; + + if(j==1) for(i=0;i<=grid[k]->xcells;i++) grid[k]->x[i] = next_real(&cp); + if(j==2) for(i=0;i<=grid[k]->ycells;i++) grid[k]->y[i] = next_real(&cp); + if(j==3) for(i=0;i<=grid[k]->zcells;i++) grid[k]->z[i] = next_real(&cp); + } + + printf("Loading material structure\n"); + + for(j=grid[k]->ycells;j>=1;j--) { + + Getline(line,in); + cp=line; + for(i=1;i<=grid[k]->xcells;i++) - grid[k]->structure[j][i] = 1; - } - - else if(strstr(command,"MINIMUM ELEMENT DIVISION")) { - if(0) printf("Loading minimum number of elements\n"); - if((*grid)[k].dimension == 1) - sscanf(params,"%d",&(*grid)[k].minxelems); - if((*grid)[k].dimension == 2) - sscanf(params,"%d %d",&(*grid)[k].minxelems,&(*grid)[k].minyelems); - if((*grid)[k].dimension == 3) - sscanf(params,"%d %d %d",&(*grid)[k].minxelems,&(*grid)[k].minyelems,&(*grid)[k].minzelems); - } - - else if(strstr(command,"SUBCELL LIMITS 1")) { - if(0) printf("Loading [%d] subcell limits in X-direction\n",grid[k]->xcells+1); - cp = params; - for(i=0;i<=grid[k]->xcells;i++) grid[k]->x[i] = next_real(&cp); - } - else if(strstr(command,"SUBCELL LIMITS 2")) { - if(0) printf("Loading [%d] subcell limits in Y-direction\n",grid[k]->ycells+1); - cp = params; - for(i=0;i<=grid[k]->ycells;i++) grid[k]->y[i] = next_real(&cp); - } - else if(strstr(command,"SUBCELL LIMITS 3")) { - if(0) printf("Loading [%d] subcell limits in Z-direction\n",grid[k]->zcells+1); - cp = params; - for(i=0;i<=grid[k]->zcells;i++) grid[k]->z[i] = next_real(&cp); - } + grid[k]->structure[j][i] = next_int(&cp); + } - else if(strstr(command,"SUBCELL SIZES 1")) { - if(0) printf("Loading [%d] subcell sizes in X-direction\n",grid[k]->xcells); - cp = params; - for(i=1;i<=grid[k]->xcells;i++) grid[k]->x[i] = next_real(&cp); - for(i=1;i<=grid[k]->xcells;i++) grid[k]->x[i] = grid[k]->x[i-1] + grid[k]->x[i]; - } - else if(strstr(command,"SUBCELL SIZES 2")) { - if(0) printf("Loading [%d] subcell sizes in Y-direction\n",grid[k]->ycells); - cp = params; - for(i=1;i<=grid[k]->ycells;i++) grid[k]->y[i] = next_real(&cp); - for(i=1;i<=grid[k]->ycells;i++) grid[k]->y[i] = grid[k]->y[i-1] + grid[k]->y[i]; - } - else if(strstr(command,"SUBCELL SIZES 3")) { - if(0) printf("Loading [%d] subcell sizes in Z-direction\n",grid[k]->zcells); - cp = params; - for(i=1;i<=grid[k]->zcells;i++) grid[k]->z[i] = next_real(&cp); - for(i=1;i<=grid[k]->zcells;i++) grid[k]->z[i] = grid[k]->z[i-1] + grid[k]->z[i]; - } - - else if(strstr(command,"SUBCELL ORIGIN 1")) { - for(i=0;ix[0] + grid[k]->x[grid[k]->xcells]); - } - else if(strstr(params,"LEFT") || strstr(params,"MIN") ) { - raid = grid[k]->x[0]; - } - else if(strstr(params,"RIGHT") || strstr(params,"MAX") ) { - raid = grid[k]->x[grid[k]->xcells]; - } - else { - cp = params; - raid = next_real(&cp); - } - for(i=0;i<=grid[k]->xcells;i++) grid[k]->x[i] -= raid; - } - else if(strstr(command,"SUBCELL ORIGIN 2")) { - for(i=0;iy[0] + grid[k]->y[grid[k]->ycells]); - } - else if(strstr(params,"LEFT")) { - raid = grid[k]->y[0]; - } - else if(strstr(params,"RIGHT")) { - raid = grid[k]->y[grid[k]->ycells]; - } - else { - cp = params; - raid = next_real(&cp); - } - for(i=0;i<=grid[k]->ycells;i++) grid[k]->y[i] -= raid; - } - else if(strstr(command,"SUBCELL ORIGIN 3")) { - for(i=0;iz[0] + grid[k]->z[grid[k]->zcells]); - } - else if(strstr(params,"LEFT")) { - raid = grid[k]->z[0]; - } - else if(strstr(params,"RIGHT")) { - raid = grid[k]->z[grid[k]->zcells]; - } - else { - cp = params; - raid = next_real(&cp); - } - for(i=0;i<=grid[k]->zcells;i++) grid[k]->z[i] -= raid; - } - - else if(strstr(command,"MATERIAL STRUCTURE")) { - if(0) printf("Loading material structure\n"); - - /* Initialize the default structure with zeros */ - for(j=grid[k]->ycells;j>=1;j--) - for(i=1;i<=grid[k]->xcells;i++) - grid[k]->structure[j][i] = 0; - - for(j=grid[k]->ycells;j>=1;j--) { - if(j < grid[k]->ycells) Getline(params,in); - cp=params; - for(i=1;i<=grid[k]->xcells;i++) - grid[k]->structure[j][i] = next_int(&cp); - } minmat = maxmat = grid[k]->structure[1][1]; for(j=grid[k]->ycells;j>=1;j--) for(i=1;i<=grid[k]->xcells;i++) { @@ -2501,736 +2717,2985 @@ int LoadElmergrid(struct GridType **grid,int *nogrids,char *prefix,int info) } if(minmat < 0) printf("LoadElmergrid: please use positive material indices.\n"); - if(maxmat > MAXMATERIALS) - printf("LoadElmergrid: material indices larger to %d may create problems.\n", - MAXMATERIALS); - } - else if(strstr(command,"MATERIALS INTERVAL")) { - sscanf(params,"%d %d",&(*grid)[k].firstmaterial,&(*grid)[k].lastmaterial); - } - - else if(strstr(command,"REVOLVE")) { - if(strstr(command,"REVOLVE RADIUS")) { - (*grid)[k].rotate = TRUE; - sscanf(params,"%le",&(*grid)[k].rotateradius2); + + mode = 0; + break; + + case 3: + case 31: + case 32: + + /* I don't know how to set this, luckily this piece of code should be obsolete */ + l = 1; + for(i=grid[k]->mappings;imappings+l;i++) { + Getline(line,in); + cp=line; + + grid[k]->mappingtype[i] = next_int(&cp); + if(mode == 32) grid[k]->mappingtype[i] += 50*SGN(grid[k]->mappingtype[i]); + + grid[k]->mappingline[i] = next_int(&cp); + grid[k]->mappinglimits[2*i] = next_real(&cp); + grid[k]->mappinglimits[2*i+1] = next_real(&cp); + grid[k]->mappingpoints[i] = next_int(&cp); + grid[k]->mappingparams[i] = Rvector(0,grid[k]->mappingpoints[i]); + for(j=0;jmappingpoints[i];j++) + grid[k]->mappingparams[i][j] = next_real(&cp); } - else if(strstr(command,"REVOLVE BLOCKS")) { - (*grid)[k].rotate = TRUE; - sscanf(params,"%d",&(*grid)[k].rotateblocks); + + printf("Loaded %d geometry mappings\n",l); + grid[k]->mappings += l; + + mode = 0; + break; + + case 4: /* NUMBERING */ + if(strstr(line,"HORIZ")) grid[k]->numbering = NUMBER_XY; + if(strstr(line,"VERTI")) grid[k]->numbering = NUMBER_YX; + mode = 0; + break; + + case 5: /* MESHING */ + if((*nogrids) >= MAXCASES) { + printf("There are more grids than was allocated for!\n"); + printf("Ignoring meshes starting from %d\n.",(*nogrids)+1); + goto end; } - else if(strstr(command,"REVOLVE IMPROVE")) { - (*grid)[k].rotate = TRUE; - sscanf(params,"%le",&(*grid)[k].rotateimprove); + (*nogrids)++; + printf("Loading element meshing no %d\n",*nogrids); + k = *nogrids - 1; + if(k > nogrids0) (*grid)[k] = (*grid)[k-1]; + mode = 0; + break; + + case 6: /* ELEMENTS */ + sscanf(line,"%d",&(*grid)[k].wantedelems); + mode = 0; + break; + + case 7: /* NODES */ + sscanf(line,"%d",&(*grid)[k].nonodes); + + (*grid)[k].elemmidpoints = FALSE; + if((*grid)[k].nonodes == 4) + (*grid)[k].elemorder = 1; + if((*grid)[k].nonodes == 8) + (*grid)[k].elemorder = 2; + if((*grid)[k].nonodes == 16) + (*grid)[k].elemorder = 3; + + if((*grid)[k].nonodes == 9) { + (*grid)[k].elemorder = 2; + (*grid)[k].elemmidpoints = TRUE; } - else if(strstr(command,"REVOLVE RADIUS")) { - sscanf(params,"%le",&(*grid)[k].polarradius); + if((*grid)[k].nonodes == 12) { + (*grid)[k].elemorder = 3; + (*grid)[k].elemmidpoints = TRUE; } - else if(strstr(command,"REVOLVE CURVE DIRECT")) { - (*grid)[k].rotatecurve = TRUE; - sscanf(params,"%le",&(*grid)[k].curvezet); + + + mode = 0; + break; + + case 8: /* TRIANGLES */ + (*grid)[k].triangles = TRUE; + mode = 0; + break; + + case 17: /* SQUARES */ + (*grid)[k].triangles = FALSE; + mode = 0; + break; + + case 16: /* ELEMENTTYPE and ELEMENTCODE */ + sscanf(line,"%d",&elemcode); + if(elemcode/100 == 2) { + (*grid)[k].triangles = FALSE; + (*grid)[k].nonodes = elemcode%100; } - else if(strstr(command,"REVOLVE CURVE RADIUS")) { - (*grid)[k].rotatecurve = TRUE; - sscanf(params,"%le",&(*grid)[k].curverad); + else if(elemcode/100 == 4) { + (*grid)[k].triangles = FALSE; + (*grid)[k].nonodes = elemcode%100; } - else if(strstr(command,"REVOLVE CURVE ANGLE")) { - (*grid)[k].rotatecurve = TRUE; - sscanf(params,"%le",&(*grid)[k].curveangle); + else if(elemcode/100 == 3) { + (*grid)[k].triangles = TRUE; + if(elemcode%100 == 3) (*grid)[k].nonodes = 4; + else if(elemcode%100 == 6) (*grid)[k].nonodes = 9; + else if(elemcode%100 == 10) (*grid)[k].nonodes = 16; } - } - else if(strstr(command,"REDUCE ORDER INTERVAL")) { - sscanf(params,"%d%d",&(*grid)[k].reduceordermatmin, - &(*grid)[k].reduceordermatmax); - } - - else if(strstr(command,"BOUNDARY DEFINITION")) { - if(0) printf("Loading boundary conditions\n"); - - for(i=0;i0) Getline(params,in); - for(j=0;j 1) + for(i=0;i<=(*grid)[k].ycells;i++) (*grid)[k].y[i] *= scaling; if((*grid)[k].dimension == 3) - sscanf(params,"%le %le",&(*grid)[k].xyratio,&(*grid)[k].xzratio); + for(i=0;i<=(*grid)[k].ycells;i++) (*grid)[k].z[i] *= scaling; + + (*grid)[k].rotateradius2 *= scaling; + (*grid)[k].curverad *= scaling; + (*grid)[k].curvezet *= scaling; + mode = 0; + break; + + default: + if(0) printf("Unknown case: %s",line); + } + + } + +end: + + if(info) printf("Found %d divisions for grid\n",*nogrids); + + for(k=nogrids0;k < (*nogrids) && kdimension = 2; + if(strstr(params,"CARTESIAN 1D")) { + grid[k]->coordsystem = COORD_CART1; + grid[k]->dimension = 1; + } + else if(strstr(params,"CARTESIAN 2D")) + grid[k]->coordsystem = COORD_CART2; + else if(strstr(params,"AXISYMMETRIC")) + grid[k]->coordsystem = COORD_AXIS; + else if(strstr(params,"POLAR")) + grid[k]->coordsystem = COORD_POLAR; + else if(strstr(params,"CARTESIAN 3D")) { + grid[k]->coordsystem = COORD_CART3; + grid[k]->dimension = 3; + } + else printf("Unknown coordinate system: %s\n",params); + if(info) printf("Defining the coordinate system (%d-DIM).\n",grid[k]->dimension); + } + + else if(strstr(command,"SUBCELL DIVISIONS")) { + if(grid[k]->dimension == 1) { + sscanf(params,"%d",&(*grid)[k].xcells); + grid[k]->ycells = 1; + } + else if(grid[k]->dimension == 2) + sscanf(params,"%d %d",&(*grid)[k].xcells,&(*grid)[k].ycells); + else if(grid[k]->dimension == 3) + sscanf(params,"%d %d %d",&(*grid)[k].xcells,&(*grid)[k].ycells,&(*grid)[k].zcells); + + if(grid[k]->xcells >= MAXCELLS || grid[k]->ycells >= MAXCELLS || grid[k]->zcells >= MAXCELLS) { + printf("LoadElmergrid: Too many subcells [%d %d %d] vs. %d:\n", + grid[k]->xcells,grid[k]->ycells,grid[k]->zcells,MAXCELLS); + } + + /* Initialize the default structure with ones */ + for(j=grid[k]->ycells;j>=1;j--) + for(i=1;i<=grid[k]->xcells;i++) + grid[k]->structure[j][i] = 1; + } + + else if(strstr(command,"MINIMUM ELEMENT DIVISION")) { + if(info) printf("Loading minimum number of elements\n"); + + if((*grid)[k].dimension == 1) + sscanf(params,"%d",&(*grid)[k].minxelems); + + if((*grid)[k].dimension == 2) + sscanf(params,"%d %d",&(*grid)[k].minxelems,&(*grid)[k].minyelems); + + if((*grid)[k].dimension == 3) + sscanf(params,"%d %d %d",&(*grid)[k].minxelems, + &(*grid)[k].minyelems,&(*grid)[k].minzelems); + } + + else if(strstr(command,"SUBCELL LIMITS 1")) { + if(info) printf("Loading %d subcell limits in X-direction\n",grid[k]->xcells+1); + cp = params; + for(i=0;i<=grid[k]->xcells;i++) { + grid[k]->x[i] = next_real(&cp); + if(i > 0 && grid[k]->x[i] < grid[k]->x[i-1]) { + printf("Subcell limits 1(%d): %12.6le %12.6le\n",i,grid[k]->x[i],grid[k]->x[i-1]); + bigerror("Values for limits 1 should be a growing series, existing\n"); + } + } + } + else if(strstr(command,"SUBCELL LIMITS 2")) { + if(info) printf("Loading %d subcell limits in Y-direction\n",grid[k]->ycells+1); + cp = params; + for(i=0;i<=grid[k]->ycells;i++) { + grid[k]->y[i] = next_real(&cp); + if(i > 0 && grid[k]->y[i] < grid[k]->y[i-1]) { + printf("Subcell limits 2(%d): %12.6le %12.6le\n",i,grid[k]->y[i],grid[k]->y[i-1]); + bigerror("Values for limits should be a growing series, existing\n"); + } + } + } + else if(strstr(command,"SUBCELL LIMITS 3")) { + if(info) printf("Loading %d subcell limits in Z-direction\n",grid[k]->zcells+1); + cp = params; + for(i=0;i<=grid[k]->zcells;i++) { + grid[k]->z[i] = next_real(&cp); + if(i > 0 && grid[k]->z[i] < grid[k]->z[i-1]) { + printf("Subcell limits 3(%d): %12.6le %12.6le\n",i,grid[k]->z[i],grid[k]->z[i-1]); + bigerror("Values for limits should be a growing series, existing\n"); + } + } + } + + else if(strstr(command,"SUBCELL SIZES 1")) { + if(info) printf("Loading %d subcell sizes in X-direction\n",grid[k]->xcells); + cp = params; + for(i=1;i<=grid[k]->xcells;i++) grid[k]->x[i] = next_real(&cp); + for(i=1;i<=grid[k]->xcells;i++) grid[k]->x[i] = grid[k]->x[i-1] + grid[k]->x[i]; + } + else if(strstr(command,"SUBCELL SIZES 2")) { + if(info) printf("Loading %d subcell sizes in Y-direction\n",grid[k]->ycells); + cp = params; + for(i=1;i<=grid[k]->ycells;i++) grid[k]->y[i] = next_real(&cp); + for(i=1;i<=grid[k]->ycells;i++) grid[k]->y[i] = grid[k]->y[i-1] + grid[k]->y[i]; + } + else if(strstr(command,"SUBCELL SIZES 3")) { + if(info) printf("Loading %d subcell sizes in Z-direction\n",grid[k]->zcells); + cp = params; + for(i=1;i<=grid[k]->zcells;i++) grid[k]->z[i] = next_real(&cp); + for(i=1;i<=grid[k]->zcells;i++) grid[k]->z[i] = grid[k]->z[i-1] + grid[k]->z[i]; + } + + else if(strstr(command,"SUBCELL ORIGIN 1")) { + for(i=0;ix[0] + grid[k]->x[grid[k]->xcells]); + } + else if(strstr(params,"LEFT") || strstr(params,"MIN") ) { + raid = grid[k]->x[0]; + } + else if(strstr(params,"RIGHT") || strstr(params,"MAX") ) { + raid = grid[k]->x[grid[k]->xcells]; + } + else { + cp = params; + raid = next_real(&cp); + } + for(i=0;i<=grid[k]->xcells;i++) grid[k]->x[i] -= raid; + } + else if(strstr(command,"SUBCELL ORIGIN 2")) { + for(i=0;iy[0] + grid[k]->y[grid[k]->ycells]); + } + else if(strstr(params,"LEFT")) { + raid = grid[k]->y[0]; + } + else if(strstr(params,"RIGHT")) { + raid = grid[k]->y[grid[k]->ycells]; + } + else { + cp = params; + raid = next_real(&cp); + } + for(i=0;i<=grid[k]->ycells;i++) grid[k]->y[i] -= raid; + } + else if(strstr(command,"SUBCELL ORIGIN 3")) { + for(i=0;iz[0] + grid[k]->z[grid[k]->zcells]); + } + else if(strstr(params,"LEFT")) { + raid = grid[k]->z[0]; + } + else if(strstr(params,"RIGHT")) { + raid = grid[k]->z[grid[k]->zcells]; + } + else { + cp = params; + raid = next_real(&cp); + } + for(i=0;i<=grid[k]->zcells;i++) grid[k]->z[i] -= raid; + } + + else if(strstr(command,"MATERIAL STRUCTURE")) { + if(info) printf("Loading material structure\n"); + + /* Initialize the default structure with zeros */ + for(j=grid[k]->ycells;j>=1;j--) + for(i=1;i<=grid[k]->xcells;i++) + grid[k]->structure[j][i] = 0; + + for(j=grid[k]->ycells;j>=1;j--) { + if(j < grid[k]->ycells) Getline(params,in); + cp=params; + for(i=1;i<=grid[k]->xcells;i++) + grid[k]->structure[j][i] = next_int(&cp); + } + minmat = maxmat = grid[k]->structure[1][1]; + for(j=grid[k]->ycells;j>=1;j--) + for(i=1;i<=grid[k]->xcells;i++) { + if(minmat > grid[k]->structure[j][i]) + minmat = grid[k]->structure[j][i]; + if(maxmat < grid[k]->structure[j][i]) + maxmat = grid[k]->structure[j][i]; + } + if(minmat < 0) + printf("LoadElmergrid: please use positive material indices.\n"); + if(maxmat > MAXBODYID) + printf("LoadElmergrid: material indices larger to %d may create problems.\n", + MAXBODYID); + printf("LoadElmergrid: materials interval is [%d,%d]\n",minmat,maxmat); + + grid[k]->maxmaterial = maxmat; + } + else if(strstr(command,"MATERIALS INTERVAL")) { + sscanf(params,"%d %d",&(*grid)[k].firstmaterial,&(*grid)[k].lastmaterial); + } + + else if(strstr(command,"REVOLVE")) { + if(0) printf("revolve: %s %s\n",command,params); + + if(strstr(command,"REVOLVE RADIUS")) { + (*grid)[k].rotate = TRUE; + sscanf(params,"%le",&(*grid)[k].rotateradius2); + } + else if(strstr(command,"REVOLVE BLOCKS")) { + (*grid)[k].rotate = TRUE; + sscanf(params,"%d",&(*grid)[k].rotateblocks); + } + else if(strstr(command,"REVOLVE IMPROVE")) { + (*grid)[k].rotate = TRUE; + sscanf(params,"%le",&(*grid)[k].rotateimprove); + } + else if(strstr(command,"REVOLVE RADIUS")) { + sscanf(params,"%le",&(*grid)[k].polarradius); + } + else if(strstr(command,"REVOLVE CURVE DIRECT")) { + (*grid)[k].rotatecurve = TRUE; + sscanf(params,"%le",&(*grid)[k].curvezet); + } + else if(strstr(command,"REVOLVE CURVE RADIUS")) { + (*grid)[k].rotatecurve = TRUE; + sscanf(params,"%le",&(*grid)[k].curverad); + } + else if(strstr(command,"REVOLVE CURVE ANGLE")) { + (*grid)[k].rotatecurve = TRUE; + sscanf(params,"%le",&(*grid)[k].curveangle); + } + } + + else if(strstr(command,"REDUCE ORDER INTERVAL")) { + sscanf(params,"%d%d",&(*grid)[k].reduceordermatmin, + &(*grid)[k].reduceordermatmax); + } + + else if(strstr(command,"BOUNDARY DEFINITION")) { + printf("Loading boundary conditions\n"); + + for(i=0;i0) Getline(params,in); + for(j=0;j1) Getline(params,in); + sscanf(params,"%d %d %d\n", + &(*grid)[k].zfirstmaterial[i],&(*grid)[k].zlastmaterial[i],&(*grid)[k].zmaterial[i]); + } + } */ + + else if(strstr(command,"GEOMETRY MAPPINGS")) { + if(k > 0) (*grid)[k].mappings = 0; + + for(i=0;i(*grid)[k].mappings) Getline(params,in); + + if(strstr(params,"END")) break; + cp=params; + (*grid)[k].mappingtype[i] = next_int(&cp); +#if 0 + (*grid)[k].mappingtype[i] += 50*SGN((*grid)[k].mappingtype[i]); +#endif + (*grid)[k].mappingline[i] = next_int(&cp); + (*grid)[k].mappinglimits[2*i] = next_real(&cp); + (*grid)[k].mappinglimits[2*i+1] = next_real(&cp); + (*grid)[k].mappingpoints[i] = next_int(&cp); + (*grid)[k].mappingparams[i] = Rvector(0,(*grid)[k].mappingpoints[i]); + for(j=0;j<(*grid)[k].mappingpoints[i];j++) + (*grid)[k].mappingparams[i][j] = next_real(&cp); + } + printf("Loaded %d geometry mappings\n",i); + (*grid)[k].mappings = i; + } + + else if(strstr(command,"END") ) { + if(0) printf("End of field\n"); + } + + else if(strstr(command,"START NEW MESH")) { + if((*nogrids) >= MAXCASES) { + printf("There are more grids than was allocated for!\n"); + printf("Ignoring meshes starting from %d\n.",(*nogrids)+1); + goto end; + } + (*nogrids)++; + printf("\nLoading element meshing no %d\n",*nogrids); + k = *nogrids - 1; + if(k > nogrids0) (*grid)[k] = (*grid)[k-1]; + } + + else { + if(0) printf("Unknown command: %s",command); + } + } + +end: + + if(info) printf("Found %d divisions for grid\n",*nogrids); + + for(k=nogrids0;k < (*nogrids) && krelh = 1.0; + eg->inmethod = 0; + eg->outmethod = 0; + eg->silent = FALSE; + eg->nofilesin = 1; + eg->unitemeshes = FALSE; + eg->unitenooverlap = FALSE; + eg->triangles = FALSE; + eg->triangleangle = 0.0; + eg->rotate = FALSE; + eg->polar = FALSE; + eg->cylinder = FALSE; + eg->usenames = TRUE; + eg->layers = 0; + eg->layereps = 0.0; + eg->layermove = 0; + eg->partitions = 0; + eg->elements3d = 0; + eg->nodes3d = 0; + eg->metis = 0; + eg->metiscontig = FALSE; + eg->metisseed = 0; + eg->partopt = 0; + eg->partoptim = FALSE; + eg->partbcoptim = TRUE; + eg->partjoin = 0; + for(i=0;iparthalo[i] = FALSE; + eg->partitionindirect = FALSE; + eg->reduce = FALSE; + eg->increase = FALSE; + eg->translate = FALSE; + eg->isoparam = FALSE; + eg->removelowdim = FALSE; + eg->removeintbcs = FALSE; + eg->removeunused = FALSE; + eg->dim = 3; + eg->center = FALSE; + eg->scale = FALSE; + eg->order = FALSE; + eg->boundbounds = 0; + eg->saveinterval[0] = eg->saveinterval[1] = eg->saveinterval[2] = 0; + eg->bulkbounds = 0; + eg->partorder = FALSE; + eg->findsides = FALSE; + eg->parthypre = FALSE; + eg->partdual = FALSE; + eg->partbcz = 0; + eg->partbcr = 0; + eg->partbclayers = 1; + eg->partbcmetis = 0; + eg->partbw = FALSE; + eg->saveboundaries = TRUE; + eg->vtuone = FALSE; + eg->timeron = FALSE; + eg->nosave = FALSE; + eg->nooverwrite = FALSE; + eg->merge = FALSE; + eg->bcoffset = FALSE; + eg->periodic = 0; + eg->periodicdim[0] = 0; + eg->periodicdim[1] = 0; + eg->periodicdim[2] = 0; + eg->bulkorder = FALSE; + eg->boundorder = FALSE; + eg->sidemappings = 0; + eg->bulkmappings = 0; + eg->coordinatemap[0] = eg->coordinatemap[1] = eg->coordinatemap[2] = 0; + eg->clone[0] = eg->clone[1] = eg->clone[2] = 0; + eg->mirror[0] = eg->mirror[1] = eg->mirror[2] = 0; + eg->cloneinds = FALSE; + eg->mirrorbc = 0; + eg->decimals = 12; + eg->discont = 0; + eg->connect = 0; + eg->connectboundsnosets = 0; + + eg->rotatecurve = FALSE; + eg->curverad = 0.5; + eg->curveangle = 90.0; + eg->curvezet = 0.0; + eg->parttol = 0.0; + + for(i=0;isidebulk[i] = 0; +} + + + + +int InlineParameters(struct ElmergridType *eg,int argc,char *argv[],int first,int info) +{ + int arg,i,dim; + char command[MAXLINESIZE]; + + dim = eg->dim; + + printf("Elmergrid reading in-line arguments\n"); + + /* Type of input file */ + if(first > 3) { + for(i=0;iinmethod = i; + break; + } + } + if(i>MAXINMETHODS) eg->inmethod = atoi(argv[1]); + + + /* Type of output file (fewer options) */ + strcpy(command,argv[2]); + for(i=0;ioutmethod = i; + break; + } + } + if(i>MAXOUTMETHODS) eg->outmethod = atoi(argv[2]); + + /* Name of output file */ + strcpy(eg->filesin[0],argv[3]); + strcpy(eg->filesout[0],eg->filesin[0]); + strcpy(eg->infofile,eg->filesin[0]); + } + + + /* The optional inline parameters */ + + for(arg=first;arg silent = TRUE; + info = FALSE; + } + + if(strcmp(argv[arg],"-verbose") == 0) { + eg->silent = FALSE; + info = TRUE; + } + + if(strcmp(argv[arg],"-in") ==0 ) { + if(arg+1 >= argc) { + printf("The secondary input file name is required as a parameter\n"); + return(1); + } + else { + strcpy(eg->filesin[eg->nofilesin],argv[arg+1]); + printf("A secondary input file %s will be loaded.\n",eg->filesin[eg->nofilesin]); + eg->nofilesin++; + } + } + + if(strcmp(argv[arg],"-out") == 0) { + if(arg+1 >= argc) { + printf("The output name is required as a parameter\n"); + return(2); + } + else { + strcpy(eg->filesout[0],argv[arg+1]); + } + } + + + if(strcmp(argv[arg],"-decimals") == 0) { + eg->decimals = atoi(argv[arg+1]); + } + + if(strcmp(argv[arg],"-triangles") ==0) { + eg->triangles = TRUE; + printf("The rectangles will be split to triangles.\n"); + if(arg+1 < argc) { + if(strcmp(argv[arg+1],"-")) { + eg->triangleangle = atof(argv[arg+1]); + } + } + } + + if(strcmp(argv[arg],"-merge") == 0) { + if(arg+1 >= argc) { + printf("Give a parameter for critical distance.\n"); + return(3); + } + else { + eg->merge = TRUE; + eg->cmerge = atof(argv[arg+1]); + } + } + + if(strcmp(argv[arg],"-relh") == 0) { + if(arg+1 >= argc) { + printf("Give a relative mesh density related to the specifications\n"); + return(3); + } + else { + eg->relh = atof(argv[arg+1]); + } + } + + if(strcmp(argv[arg],"-order") == 0) { + if(arg+dim >= argc) { + printf("Give %d parameters for the order vector.\n",dim); + return(4); + } + else { + eg->order = TRUE; + eg->corder[0] = atof(argv[arg+1]); + eg->corder[1] = atof(argv[arg+2]); + if(dim==3) eg->corder[2] = atof(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-parttol") == 0) { + if(arg+1 >= argc) { + printf("Give a tolerance for gemetric partition algorithms\n"); + return(3); + } + else { + eg->parttol = atof(argv[arg+1]); + } + } + + if(strcmp(argv[arg],"-autoorder") == 0) { + eg->order = 2; + } + + if(strcmp(argv[arg],"-halo") == 0) { + eg->parthalo[1] = TRUE; + } + if(strcmp(argv[arg],"-halobc") == 0) { + eg->parthalo[2] = TRUE; + } + if(strcmp(argv[arg],"-halodb") == 0) { + eg->parthalo[1] = TRUE; + eg->parthalo[2] = TRUE; + } + if(strcmp(argv[arg],"-haloz") == 0) { + eg->parthalo[3] = TRUE; + } + if(strcmp(argv[arg],"-halor") == 0) { + eg->parthalo[3] = TRUE; + } + if(strcmp(argv[arg],"-halogreedy") == 0) { + eg->parthalo[4] = TRUE; + } + if(strcmp(argv[arg],"-indirect") == 0) { + eg->partitionindirect = TRUE; + } + if(strcmp(argv[arg],"-metisorder") == 0) { + eg->order = 3; + } + if(strcmp(argv[arg],"-centralize") == 0) { + eg->center = TRUE; + } + if(strcmp(argv[arg],"-scale") == 0) { + if(arg+dim >= argc) { + printf("Give %d parameters for the scaling.\n",dim); + return(5); + } + else { + eg->scale = TRUE; + eg->cscale[0] = atof(argv[arg+1]); + eg->cscale[1] = atof(argv[arg+2]); + if(dim==3) eg->cscale[2] = atof(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-translate") == 0) { + if(arg+dim >= argc) { + printf("Give %d parameters for the translate vector.\n",dim); + return(6); + } + else { + eg->translate = TRUE; + eg->ctranslate[0] = atof(argv[arg+1]); + eg->ctranslate[1] = atof(argv[arg+2]); + if(dim==3) eg->ctranslate[2] = atof(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-saveinterval") == 0) { + if(arg+dim >= argc) { + printf("Give min, max and step for the interval.\n"); + return(7); + } + else { + eg->saveinterval[0] = atoi(argv[arg+1]); + eg->saveinterval[1] = atoi(argv[arg+2]); + eg->saveinterval[2] = atoi(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-rotate") == 0 || strcmp(argv[arg],"-rotate") == 0) { + if(arg+dim >= argc) { + printf("Give three parameters for the rotation angles.\n"); + return(8); + } + else { + eg->rotate = TRUE; + eg->crotate[0] = atof(argv[arg+1]); + eg->crotate[1] = atof(argv[arg+2]); + eg->crotate[2] = atof(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-clone") == 0) { + if(arg+dim >= argc) { + printf("Give the number of clones in each %d directions.\n",dim); + return(9); + } + else { + eg->clone[0] = atoi(argv[arg+1]); + eg->clone[1] = atoi(argv[arg+2]); + if(dim == 3) eg->clone[2] = atoi(argv[arg+3]); + } + } + if(strcmp(argv[arg],"-clonesize") == 0) { + if(arg+dim >= argc) { + printf("Give the clone size in each %d directions.\n",dim); + return(10); + } + else { + eg->clonesize[0] = atof(argv[arg+1]); + eg->clonesize[1] = atof(argv[arg+2]); + if(dim == 3) eg->clonesize[2] = atof(argv[arg+3]); + } + } + if(strcmp(argv[arg],"-cloneinds") == 0) { + eg->cloneinds = TRUE; + } + if(strcmp(argv[arg],"-mirror") == 0) { + if(arg+dim >= argc) { + printf("Give the symmetry of the coordinate directions, eg. 1 1 0\n"); + } + else { + eg->mirror[0] = atoi(argv[arg+1]); + eg->mirror[1] = atoi(argv[arg+2]); + if(dim == 3) eg->mirror[2] = atoi(argv[arg+3]); + } + } + if(strcmp(argv[arg],"-mirrorbc") == 0) { + if(arg+1 >= argc) { + printf("Give the number of symmetry BC.\n"); + return(11); + } + else { + eg->mirrorbc = atoi(argv[arg+1]); + } + } + + if(strcmp(argv[arg],"-unite") == 0) { + eg->unitemeshes = TRUE; + printf("The meshes will be united.\n"); + } + if(strcmp(argv[arg],"-unitenooverlap") == 0) { + eg->unitemeshes = TRUE; + eg->unitenooverlap = TRUE; + printf("The meshes will be united without overlap in BCs or bodies.\n"); + } + + if(strcmp(argv[arg],"-nonames") == 0) { + eg->usenames = FALSE; + printf("Names will be omitted even if they would exist\n"); + } + + if(strcmp(argv[arg],"-removelowdim") == 0) { + eg->removelowdim = TRUE; + printf("Lower dimensional boundaries will be removed\n"); + } + + if(strcmp(argv[arg],"-removeintbcs") == 0) { + eg->removeintbcs = TRUE; + printf("Lower dimensional boundaries will be removed\n"); + } + + if(strcmp(argv[arg],"-removeunused") == 0) { + eg->removeunused = TRUE; + printf("Nodes that do not appear in any element will be removed\n"); + } + + if(strcmp(argv[arg],"-autoclean") == 0) { + eg->removelowdim = TRUE; + eg->bulkorder = TRUE; + eg->boundorder = TRUE; + eg->removeunused = TRUE; + printf("Lower dimensional boundaries will be removed\n"); + printf("Materials and boundaries will be renumbered\n"); + printf("Nodes that do not appear in any element will be removed\n"); + } + + if(strcmp(argv[arg],"-polar") == 0) { + eg->polar = TRUE; + printf("Making transformation to polar coordinates.\n"); + if(arg+1 >= argc) { + printf("The preferred radius is required as a parameter\n"); + eg->polarradius = 1.0; + } + else { + eg->polarradius = atoi(argv[arg+1]); + } + } + + if(strcmp(argv[arg],"-cylinder") == 0) { + eg->cylinder = TRUE; + printf("Making transformation from cylindrical to cartesian coordinates.\n"); + } + + if(strcmp(argv[arg],"-reduce") == 0) { + if(arg+2 >= argc) { + printf("Give two material for the interval.\n"); + return(12); + } + else { + eg->reduce = TRUE; + eg->reducemat1 = atoi(argv[arg+1]); + eg->reducemat2 = atoi(argv[arg+2]); + } + } + if(strcmp(argv[arg],"-increase") == 0) { + eg->increase = TRUE; + } + if(strcmp(argv[arg],"-bulkorder") == 0) { + eg->bulkorder = TRUE; + } + if(strcmp(argv[arg],"-boundorder") == 0) { + eg->boundorder = TRUE; + } + if(strcmp(argv[arg],"-partition") == 0 || + strcmp(argv[arg],"-partcell") == 0 || + strcmp(argv[arg],"-partcyl") == 0 ) { + if(arg+dim >= argc) { + printf("The number of partitions in %d dims is required as parameters.\n",dim); + return(13); + } + else { + eg->partitions = 1; + eg->partdim[0] = atoi(argv[arg+1]); + eg->partdim[1] = atoi(argv[arg+2]); + if(dim == 3) eg->partdim[2] = atoi(argv[arg+3]); + eg->partitions = 1; + for(i=0;i<3;i++) { + if(eg->partdim[i] == 0) eg->partdim[i] = 1; + eg->partitions *= eg->partdim[i]; + } + eg->partopt = -1; + if( strcmp(argv[arg],"-partition") == 0 ) { + if(arg+4 < argc) + if(argv[arg+4][0] != '-') eg->partopt = atoi(argv[arg+4]); + } + else if( strcmp( argv[arg],"-partcell") == 0 ) { + eg->partopt = 2; + } else if( strcmp( argv[arg],"-partcyl") == 0 ) { + eg->partopt = 3; + } + + printf("The mesh will be partitioned geometrically to %d partitions.\n", + eg->partitions); + } + } + if(strcmp(argv[arg],"-partorder") == 0) { + if(arg+dim >= argc) { + printf("Give %d parameters for the order vector.\n",dim); + return(14); + } + else { + eg->partorder = 1; + eg->partcorder[0] = atof(argv[arg+1]); + eg->partcorder[1] = atof(argv[arg+2]); + if(dim==3) eg->partcorder[2] = atof(argv[arg+3]); + } + } + if(strcmp(argv[arg],"-partoptim") == 0) { + eg->partoptim = TRUE; + printf("Aggressive optimization will be applied to node sharing.\n"); + } + if(strcmp(argv[arg],"-partnobcoptim") == 0) { + eg->partbcoptim = FALSE; + printf("Aggressive optimization will not be applied to parent element sharing.\n"); + } + if(strcmp(argv[arg],"-partbw") == 0) { + eg->partbw = TRUE; + printf("Bandwidth will be optimized for partitions.\n"); + } + if(strcmp(argv[arg],"-parthypre") == 0) { + eg->parthypre = TRUE; + printf("Numbering of partitions will be made continuous.\n"); + } + if(strcmp(argv[arg],"-partdual") == 0) { + eg->partdual = TRUE; + printf("Using dual (elemental) graph in partitioning.\n"); + } + + if(strcmp(argv[arg],"-metis") == 0 || + strcmp(argv[arg],"-metisrec") == 0 || + strcmp(argv[arg],"-metiskway") == 0 ) { +#if USE_METIS + if(arg+1 >= argc) { + printf("The number of partitions is required as a parameter\n"); + return(15); + } + else { + eg->metis = atoi(argv[arg+1]); + printf("The mesh will be partitioned with Metis to %d partitions.\n",eg->metis); + eg->partopt = 0; + if(strcmp(argv[arg],"-metisrec") == 0) + eg->partopt = 2; + else if(strcmp(argv[arg],"-metiskway") == 0 ) + eg->partopt = 3; + else if(arg+2 < argc) + if(argv[arg+2][0] != '-') eg->partopt = atoi(argv[arg+2]); + } +#else + printf("This version of ElmerGrid was compiled without Metis library!\n"); +#endif + } + + if(strcmp(argv[arg],"-metisseed") == 0 ) { + if(arg+1 >= argc) { + printf("The random number seed is required as parameter for -metisseed!\n"); + return(15); + } + else { + eg->metisseed = atoi(argv[arg+1]); + printf("Seed for Metis partitioning routines: %d\n",eg->metisseed); + } + } + + if(strcmp(argv[arg],"-partjoin") == 0) { + if(arg+1 >= argc) { + printf("The number of partitions is required as a parameter!\n"); + return(15); + } + else { + eg->partjoin = atoi(argv[arg+1]); + printf("The results will joined using %d partitions.\n",eg->partjoin); + } + } + + if(strcmp(argv[arg],"-partconnect") == 0 || strcmp(argv[arg],"-partzbc") == 0 ) { + if(arg+1 >= argc) { + printf("The number of 1D partitions is required as a parameter!\n"); + return(15); + } + else { + eg->partbcz = atoi(argv[arg+1]); + printf("The connected BCs will be partitioned to %d partitions in Z.\n",eg->partbcz); + } + } + + if(strcmp(argv[arg],"-partrbc") == 0 ) { + if(arg+1 >= argc) { + printf("The number of 1D partitions is required as a parameter!\n"); + return(15); + } + else { + eg->partbcr = atoi(argv[arg+1]); + printf("The connected BCs will be partitioned to %d partitions in R.\n",eg->partbcr); + } + } + + if(strcmp(argv[arg],"-partlayers") == 0) { + if(arg+1 >= argc) { + printf("The number of layers to be extended is required as a parameter\n"); + return(15); + } + else { + eg->partbclayers = atoi(argv[arg+1]); + printf("The boundary partitioning will be extended by %d layers.\n",eg->partbclayers); + } + } + + if(strcmp(argv[arg],"-metiscontig") == 0 ) { + eg->metiscontig = TRUE; + } + + if(strcmp(argv[arg],"-metisconnect") == 0 || strcmp(argv[arg],"-metisbc") == 0 ) { + if(arg+1 >= argc) { + printf("The number of Metis partitions is required as a parameter\n"); + return(15); + } + else { + eg->partbcmetis = atoi(argv[arg+1]); + printf("The connected BCs will be partitioned to %d partitions by Metis.\n",eg->partbcmetis); + } + } + + if(strcmp(argv[arg],"-periodic") == 0) { + if(arg+dim >= argc) { + printf("Give the periodic coordinate directions (e.g. 1 1 0)\n"); + return(16); + } + else { + eg->periodicdim[0] = atoi(argv[arg+1]); + eg->periodicdim[1] = atoi(argv[arg+2]); + if(dim == 3) eg->periodicdim[2] = atoi(argv[arg+3]); + } + } + + if(strcmp(argv[arg],"-discont") == 0) { + if(arg+1 >= argc) { + printf("Give the discontinuous boundary conditions.\n"); + return(17); + } + else { + eg->discontbounds[eg->discont] = atoi(argv[arg+1]); + eg->discont++; + } + } + + if(strcmp(argv[arg],"-connect") == 0) { + if(arg+1 >= argc) { + printf("Give the connected boundary conditions.\n"); + return(10); + } + else { + eg->connectboundsnosets += 1; + for(i=arg+1;iconnectbounds[eg->connect] = atoi(argv[i]); + eg->connectboundsset[eg->connect] = eg->connectboundsnosets; + eg->connect++; + } + } + } + + if(strcmp(argv[arg],"-connectall") == 0) { + eg->connectboundsnosets += 1; + eg->connectbounds[eg->connect] = -1; + eg->connectboundsset[eg->connect] = eg->connectboundsnosets; + eg->connect++; + } + + if(strcmp(argv[arg],"-connectint") == 0) { + eg->connectboundsnosets += 1; + eg->connectbounds[eg->connect] = -2; + eg->connectboundsset[eg->connect] = eg->connectboundsnosets; + eg->connect++; + } + + if(strcmp(argv[arg],"-connectfree") == 0) { + eg->connectboundsnosets += 1; + eg->connectbounds[eg->connect] = -3; + eg->connectboundsset[eg->connect] = eg->connectboundsnosets; + eg->connect++; + } + + if(strcmp(argv[arg],"-boundbound") == 0) { + for(i=arg+1;i<=arg+3 && iboundbound[3*eg->boundbounds+i-(1+arg)] = atoi(argv[i]); + if((i-arg)%3 == 0) eg->boundbounds++; + } + } + if(strcmp(argv[arg],"-bulkbound") == 0) { + for(i=arg+1;i<=arg+3 && ibulkbound[3*eg->bulkbounds+i-(1+arg)] = atoi(argv[i]); + if((i-arg)%3 == 0) eg->bulkbounds++; + } + } + if(strcmp(argv[arg],"-boundtype") == 0) { + for(i=arg+1;isidemap[3*eg->sidemappings+i-1-arg] = atoi(argv[i]); + eg->sidemappings++; + } + if(strcmp(argv[arg],"-bulktype") == 0) { + for(i=arg+1;ibulkmap[3*eg->bulkmappings+i-1-arg] = atoi(argv[i]); + eg->bulkmappings++; + } + if(strcmp(argv[arg],"-coordinatemap") == 0) { + if( arg+3 >= argc ) { + printf("Give three parameters for the index permutation\n"); + return(18); + } + else { + for(i=0;i<3;i++) + eg->coordinatemap[i] = atoi(argv[arg+1+i]); + } + } + if(strcmp(argv[arg],"-layer") == 0) { + if(arg+4 >= argc) { + printf("Give four parameters for the layer: boundary, elements, thickness, ratio.\n"); + return(18); + } + else if(eg->layers == MAXBOUNDARIES) { + printf("There can only be %d layers, sorry.\n",MAXBOUNDARIES); + return(19); + } + else { + eg->layerbounds[eg->layers] = atoi(argv[arg+1]); + eg->layernumber[eg->layers] = atoi(argv[arg+2]); + eg->layerthickness[eg->layers] = atof(argv[arg+3]); + eg->layerratios[eg->layers] = atof(argv[arg+4]); + eg->layerparents[eg->layers] = 0; + eg->layers++; + } + } + + if(strcmp(argv[arg],"-layermove") == 0) { + if(arg+1 >= argc) { + printf("Give maximum number of Jacobi filters.\n"); + return(20); + } + else { + eg->layermove = atoi(argv[arg+1]); + } + } + + /* This uses a very dirty trick where the variables related to argument -layer are used + with a negative indexing */ + if(strcmp(argv[arg],"-divlayer") == 0) { + if(arg+4 >= argc) { + printf("Give four parameters for the layer: boundary, elements, relative thickness, ratio.\n"); + return(21); + } + else if(abs(eg->layers) == MAXBOUNDARIES) { + printf("There can only be %d layers, sorry.\n",MAXBOUNDARIES); + return(22); + } + else { + eg->layerbounds[abs(eg->layers)] = atoi(argv[arg+1]); + eg->layernumber[abs(eg->layers)] = atoi(argv[arg+2]); + eg->layerthickness[abs(eg->layers)] = atof(argv[arg+3]); + eg->layerratios[abs(eg->layers)] = atof(argv[arg+4]); + eg->layerparents[abs(eg->layers)] = 0; + eg->layers--; + } + } + + if(strcmp(argv[arg],"-3d") == 0) { + eg->dim = dim = 3; + } + if(strcmp(argv[arg],"-2d") == 0) { + eg->dim = dim = 2; + } + if(strcmp(argv[arg],"-1d") == 0) { + eg->dim = dim = 1; + } + + if(strcmp(argv[arg],"-isoparam") == 0) { + eg->isoparam = TRUE; + } + if(strcmp(argv[arg],"-nobound") == 0) { + eg->saveboundaries = FALSE; + } + if(strcmp(argv[arg],"-vtuone") == 0) { + eg->vtuone = TRUE; + } + if(strcmp(argv[arg],"-nosave") == 0) { + eg->nosave = TRUE; + } + if(strcmp(argv[arg],"-nooverwrite") == 0) { + eg->nooverwrite = TRUE; + } + if(strcmp(argv[arg],"-timer") == 0) { + eg->timeron = TRUE; + } + + if(strcmp(argv[arg],"-infofile") == 0) { + eg->timeron = TRUE; + if(arg+1 >= argc) { + printf("The output name is required as a parameter\n"); + return(2); + } + else { + strcpy(eg->infofile,argv[arg+1]); + } + } + + + /* The following keywords are not actively used */ + + if(strcmp(argv[arg],"-bcoffset") == 0) { + eg->bcoffset = atoi(argv[arg+1]); + } + if(strcmp(argv[arg],"-noelements") == 0) { + eg->elements3d = atoi(argv[arg+1]); + } + if(strcmp(argv[arg],"-nonodes") == 0) { + eg->nodes3d = atoi(argv[arg+1]); + } + + if(strcmp(argv[arg],"-sidefind") == 0) { + eg->findsides = 0; + for(i=arg+1;isidebulk[i-1-arg] = atoi(argv[i]); + eg->findsides++; + } + } + if(strcmp(argv[arg],"-findbound") == 0) { + eg->findsides = 0; + for(i=arg+1;i+1sidebulk[i-1-arg] = atoi(argv[i]); + eg->sidebulk[i-arg] = atoi(argv[i+1]); + eg->findsides++; + } + } + } + + { + int badpoint; + char *ptr1,*ptr2; + ptr1 = strrchr(eg->filesout[0], '.'); + if(ptr1) { + badpoint=FALSE; + ptr2 = strrchr(eg->filesout[0], '/'); + if(ptr2 && ptr2 > ptr1) badpoint = TRUE; + if(!badpoint) *ptr1 = '\0'; + } + } + + printf("Output will be saved to file %s.\n",eg->filesout[0]); + + return(0); +} + + + + +int LoadCommands(char *prefix,struct ElmergridType *eg, + struct GridType *grid, int mode,int info) +{ + char filename[MAXFILESIZE]; + char command[MAXLINESIZE],params[MAXLINESIZE],*cp; + + FILE *in=NULL; + int i,j,iostat; + + iodebug = FALSE; + + if( mode == 0) { + if (in = fopen("ELMERGRID_STARTINFO","r")) { + iostat = fscanf(in,"%s",filename); + fclose(in); + printf("Using the file %s defined in ELMERGRID_STARTINFO\n",filename); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadCommands: opening of the file '%s' wasn't successful !\n",filename); + return(1); + } + else printf("Loading ElmerGrid commands from file '%s'.\n",filename); + } + else + return(2); + } + else if(mode == 1) { + AddExtension(prefix,filename,"eg"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadCommands: opening of the file '%s' wasn't successful !\n",filename); + return(3); + } + if(info) printf("Loading ElmerGrid commands from file '%s'.\n",filename); + } + else if(mode == 2) { + AddExtension(prefix,filename,"grd"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadCommands: opening of the file '%s' wasn't successful !\n",filename); + return(4); + } + if(info) printf("\nLoading ElmerGrid commands from file '%s'.\n",filename); + } + + + + for(;;) { + + if(GetCommand(command,params,in)) { + printf("Reached the end of command file\n"); + goto end; + } + + /* If the mode is the command file mode read also the file information from the command file. */ + + if(mode <= 1) { + if(strstr(command,"INPUT FILE")) { + sscanf(params,"%s",eg->filesin[0]); + } + + else if(strstr(command,"OUTPUT FILE")) { + sscanf(params,"%s",eg->filesout[0]); + } + + else if(strstr(command,"INPUT MODE")) { + for(j=0;jinmethod = i; + break; + } + } + if(i>MAXINMETHODS) sscanf(params,"%d",&eg->inmethod); + } + + else if(strstr(command,"OUTPUT MODE")) { + for(j=0;joutmethod = i; + break; + } + } + if(i>MAXOUTMETHODS) sscanf(params,"%d",&eg->outmethod); + } + } + /* End of command file specific part */ + + + if(strstr(command,"DECIMALS")) { + sscanf(params,"%d",&eg->decimals); + } + else if(strstr(command,"BOUNDARY OFFSET")) { + sscanf(params,"%d",&eg->bcoffset); + } + else if(strstr(command,"TRIANGLES CRITICAL ANGLE")) { + sscanf(params,"%le",&eg->triangleangle); + } + else if(strstr(command,"TRIANGLES")) { + for(j=0;jtriangles = TRUE; + } + else if(strstr(command,"MERGE NODES")) { + eg->merge = TRUE; + sscanf(params,"%le",&eg->cmerge); + } + else if(strstr(command,"UNITE")) { + for(j=0;junitemeshes = TRUE; + } + else if(strstr(command,"UNITENOOVERLAP")) { + for(j=0;junitemeshes = TRUE; + eg->unitenooverlap = TRUE; + } + } + + else if(strstr(command,"ORDER NODES")) { + eg->order = TRUE; + if(eg->dim == 1) + sscanf(params,"%le",&eg->corder[0]); + else if(eg->dim == 2) + sscanf(params,"%le%le",&eg->corder[0],&eg->corder[1]); + else if(eg->dim == 3) + sscanf(params,"%le%le%le",&eg->corder[0],&eg->corder[1],&eg->corder[2]); + } + else if(strstr(command,"SCALE")) { + eg->scale = TRUE; + if(eg->dim == 1) + sscanf(params,"%le",&eg->cscale[0]); + else if(eg->dim == 2) + sscanf(params,"%le%le",&eg->cscale[0],&eg->cscale[1]); + else if(eg->dim == 3) + sscanf(params,"%le%le%le",&eg->cscale[0],&eg->cscale[1],&eg->cscale[2]); + } + else if(strstr(command,"CENTRALIZE")) { + eg->center = TRUE; + } + else if(strstr(command,"TRANSLATE")) { + eg->translate = TRUE; + if(eg->dim == 1) + sscanf(params,"%le",&eg->ctranslate[0]); + else if(eg->dim == 2) + sscanf(params,"%le%le",&eg->ctranslate[0],&eg->ctranslate[1]); + else if(eg->dim == 3) + sscanf(params,"%le%le%le",&eg->ctranslate[0],&eg->ctranslate[1],&eg->ctranslate[2]); + } + else if(strstr(command,"ROTATE MESH")) { + eg->rotate = TRUE; + sscanf(params,"%le%le%le",&eg->crotate[0],&eg->crotate[1],&eg->crotate[2]); + } + else if(strstr(command,"CLONE")) { + if(strstr(command,"CLONE SIZE")) { + if(eg->dim == 1) + sscanf(params,"%le",&eg->clonesize[0]); + else if(eg->dim == 2) + sscanf(params,"%le%le",&eg->clonesize[0],&eg->clonesize[1]); + else if(eg->dim == 3) + sscanf(params,"%le%le%le",&eg->clonesize[0],&eg->clonesize[1],&eg->clonesize[2]); + } + else { + if(eg->dim == 1) + sscanf(params,"%d",&eg->clone[0]); + else if(eg->dim == 2) + sscanf(params,"%d%d",&eg->clone[0],&eg->clone[1]); + else if(eg->dim == 3) + sscanf(params,"%d%d%d",&eg->clone[0],&eg->clone[1],&eg->clone[2]); + } + } + else if(strstr(command,"MERGE")) { + eg->merge = TRUE; + sscanf(params,"%le",&eg->cmerge); + } + else if(strstr(command,"MIRROR")) { + if(eg->dim == 1) + sscanf(params,"%d",&eg->mirror[0]); + else if(eg->dim == 2) + sscanf(params,"%d%d",&eg->mirror[0],&eg->mirror[1]); + else if(eg->dim == 3) + sscanf(params,"%d%d%d",&eg->mirror[0],&eg->mirror[1],&eg->mirror[2]); + } + else if(strstr(command,"POLAR RADIUS")) { + eg->polar = TRUE; + sscanf(params,"%le",&eg->polarradius); + } + else if(strstr(command,"CYLINDER")) { + for(j=0;jcylinder = TRUE; + } + else if(strstr(command,"REDUCE DEGREE")) { + eg->reduce = TRUE; + sscanf(params,"%d%d",&eg->reducemat1,&eg->reducemat2); + } + else if(strstr(command,"INCREASE DEGREE")) { + for(j=0;jincrease = TRUE; + } + else if(strstr(command,"METIS OPTION")) { +#if USE_METIS + sscanf(params,"%d",&eg->partopt); +#else + printf("This version of ElmerGrid was compiled without Metis library!\n"); +#endif + } + else if(strstr(command,"METIS")) { +#if USE_METIS + sscanf(params,"%d",&eg->metis); +#else + printf("This version of ElmerGrid was compiled without Metis library!\n"); +#endif + } + else if(strstr(command,"METISKWAY")) { +#if USE_METIS + sscanf(params,"%d",&eg->metis); + eg->partopt = 3; +#else + printf("This version of ElmerGrid was compiled without Metis library!\n"); +#endif + } + else if(strstr(command,"METISREC")) { +#if USE_METIS + sscanf(params,"%d",&eg->metis); + eg->partopt = 2; +#else + printf("This version of ElmerGrid was compiled without Metis library!\n"); +#endif + } + else if(strstr(command,"PARTITION DUAL")) { + for(j=0;jpartdual = TRUE; + } + else if(strstr(command,"PARTJOIN")) { + sscanf(params,"%d",&eg->partjoin); + } + else if(strstr(command,"PARTITION ORDER")) { + eg->partorder = 1; + if(eg->dim == 2) sscanf(params,"%le%le",&eg->partcorder[0],&eg->partcorder[1]); + if(eg->dim == 3) sscanf(params,"%le%le%le",&eg->partcorder[0], + &eg->partcorder[1],&eg->partcorder[2]); + } + else if(strstr(command,"PARTITION") || strstr(command,"PARTCYL") || strstr(command,"PARTCELL")) { + if(eg->dim == 2) sscanf(params,"%d%d",&eg->partdim[0],&eg->partdim[1]); + if(eg->dim == 3) sscanf(params,"%d%d%d",&eg->partdim[0],&eg->partdim[1],&eg->partdim[2]); + eg->partitions = 1; + for(i=0;idim;i++) { + if(eg->partdim[i] < 1) eg->partdim[i] = 1; + eg->partitions *= eg->partdim[i]; + } + if( strstr(command,"PARTCYL") ) eg->partopt = 3; + if( strstr(command,"PARTCCELL") ) eg->partopt = 2; + } + else if(strstr(command,"PERIODIC")) { + if(eg->dim == 2) sscanf(params,"%d%d",&eg->periodicdim[0],&eg->periodicdim[1]); + if(eg->dim == 3) sscanf(params,"%d%d%d",&eg->periodicdim[0], + &eg->periodicdim[1],&eg->periodicdim[2]); + } + else if(strstr(command,"HALO")) { + for(j=0;jparthalo[1] = TRUE; + } + else if(strstr(command,"BOUNDARY HALO")) { + for(j=0;jparthalo[2] = TRUE; + } + else if(strstr(command,"EXTRUDED HALO")) { + for(j=0;jparthalo[3] = TRUE; + } + else if(strstr(command,"GREEDY HALO")) { + for(j=0;jparthalo[4] = TRUE; + } + else if(strstr(command,"PARTBW")) { + for(j=0;jpartbw = TRUE; + } + else if(strstr(command,"PARTHYPRE")) { + for(j=0;jparthypre = TRUE; + } + else if(strstr(command,"INDIRECT")) { + for(j=0;jpartitionindirect = TRUE; + } + else if(strstr(command,"BOUNDARY TYPE MAPPINGS")) { + for(i=0;i0) Getline(params,in); + for(j=0;jsidemap[3*i],&eg->sidemap[3*i+1],&eg->sidemap[3*i+2]); + } + printf("Found %d boundary type mappings\n",i); + eg->sidemappings = i; + } + else if(strstr(command,"BULK TYPE MAPPINGS")) { + for(i=0;i0) Getline(params,in); + for(j=0;jbulkmap[3*i],&eg->bulkmap[3*i+1],&eg->bulkmap[3*i+2]); + } + printf("Found %d bulk type mappings\n",i); + eg->bulkmappings = i; + } + else if(strstr(command,"COORDINATE MAPPING")) { + sscanf(params,"%d%d%d",&eg->coordinatemap[0],&eg->coordinatemap[1],&eg->coordinatemap[2]); + } + else if(strstr(command,"BOUNDARY BOUNDARY")) { + for(i=0;i0) Getline(params,in); + for(j=0;jboundbound[3*i+2],&eg->boundbound[3*i],&eg->boundbound[3*i+1]); + } + printf("Found %d boundary boundary definitions\n",i); + eg->boundbounds = i; + } + else if(strstr(command,"MATERIAL BOUNDARY")) { + for(i=0;i0) Getline(params,in); + for(j=0;jbulkbound[3*i+2],&eg->bulkbound[3*i],&eg->bulkbound[3*i+1]); + } + printf("Found %d material boundary definitions\n",i); + eg->bulkbounds = i; + } + + else if(strstr(command,"RENUMBER BOUNDARY")) { + for(i=0;isidemap[3*i],&eg->sidemap[3*i+1],&eg->sidemap[3*i+2]); + } + printf("Found %d boundary mappings\n",i); + eg->sidemappings = i; + } + else if(strstr(command,"RENUMBER MATERIAL")) { + for(i=0;ibulkmap[3*i],&eg->bulkmap[3*i+1],&eg->bulkmap[3*i+2]); + } + printf("Found %d material mappings\n",i); + eg->bulkmappings = i; } - - else if(strstr(command,"ELEMENT RATIOS 1")) { - cp = params; - for(i=1;i<=(*grid)[k].xcells;i++) (*grid)[k].xexpand[i] = next_real(&cp); + + else if(strstr(command,"BOUNDARY LAYER")) { + if(strstr(command,"BOUNDARY LAYER MOVE")) { + sscanf(params,"%d",&eg->layermove); + } + else if(strstr(command,"BOUNDARY LAYER EPSILON")) { + sscanf(params,"%le",&eg->layereps); + } + else { + for(i=0;i0) Getline(params,in); + for(j=0;jlayerbounds[i] = next_int(&cp); + eg->layernumber[i] = next_int(&cp); + eg->layerthickness[i] = next_real(&cp); + eg->layerratios[i] = next_real(&cp); + eg->layerparents[i] = next_int(&cp); + } + printf("Found %d boundary layers\n",i); + eg->layers = i; + } } - else if(strstr(command,"ELEMENT RATIOS 2")) { - cp = params; - for(i=1;i<=(*grid)[k].ycells;i++) (*grid)[k].yexpand[i] = next_real(&cp); + else if(strstr(command,"REMOVE LOWER DIMENSIONAL BOUNDARIES")) { + for(j=0;jremovelowdim = TRUE; } - else if(strstr(command,"ELEMENT RATIOS 3")) { - cp = params; - for(i=1;i<=(*grid)[k].zcells;i++) (*grid)[k].zexpand[i] = next_real(&cp); + else if(strstr(command,"REMOVE INTERNAL BOUNDARIES")) { + for(j=0;jremoveintbcs = TRUE; } - - else if(strstr(command,"ELEMENT DENSITIES 1")) { - cp = params; - for(i=1;i<=(*grid)[k].xcells;i++) (*grid)[k].xdens[i] = next_real(&cp); + else if(strstr(command,"REMOVE UNUSED NODES")) { + for(j=0;jremoveunused = TRUE; } - else if(strstr(command,"ELEMENT DENSITIES 2")) { - cp = params; - for(i=1;i<=(*grid)[k].ycells;i++) (*grid)[k].ydens[i] = next_real(&cp); + else if(strstr(command,"NO MESH NAMES")) { + for(j=0;jusenames = FALSE; } - else if(strstr(command,"ELEMENT DENSITIES 3")) { - cp = params; - for(i=1;i<=(*grid)[k].zcells;i++) (*grid)[k].zdens[i] = next_real(&cp); + else if(strstr(command,"REORDER MATERIAL")) { + for(j=0;jbulkorder = TRUE; } - - else if(strstr(command,"ELEMENT DIVISIONS 1")) { - cp = params; - for(i=1;i<=(*grid)[k].xcells;i++) (*grid)[k].xelems[i] = next_int(&cp); - (*grid)[k].autoratio = 0; + else if(strstr(command,"REORDER BOUNDARY")) { + for(j=0;jboundorder = TRUE; } - else if(strstr(command,"ELEMENT DIVISIONS 2")) { - cp = params; - for(i=1;i<=(*grid)[k].ycells;i++) (*grid)[k].yelems[i] = next_int(&cp); - (*grid)[k].autoratio = 0; + else if(strstr(command,"DIMENSION")) { + sscanf(params,"%d",&eg->dim); } - else if(strstr(command,"ELEMENT DIVISIONS 3")) { - cp = params; - for(i=1;i<=(*grid)[k].zcells;i++) (*grid)[k].zelems[i] = next_int(&cp); - (*grid)[k].autoratio = 0; + else if(strstr(command,"ISOPARAMETRIC")) { + for(j=0;jisoparam = TRUE; } - - else if(strstr(command,"EXTRUDED STRUCTURE")) { - for(i=1;i<=(*grid)[k].zcells;i++) { - if(i>1) Getline(params,in); - sscanf(params,"%d %d %d\n", - &(*grid)[k].zfirstmaterial[i],&(*grid)[k].zlastmaterial[i],&(*grid)[k].zmaterial[i]); - } + else if(strstr(command,"NO BOUNDARY")) { + for(j=0;jsaveboundaries = FALSE; } - - else if(strstr(command,"GEOMETRY MAPPINGS")) { - if(k > 0) (*grid)[k].mappings = 0; - + else if(strstr(command,"LAYERED BOUNDARIES")) { for(i=0;i(*grid)[k].mappings) Getline(params,in); - - if(strstr(params,"END")) break; - cp=params; - (*grid)[k].mappingtype[i] = next_int(&cp); -#if 0 - (*grid)[k].mappingtype[i] += 50*SGN((*grid)[k].mappingtype[i]); -#endif - (*grid)[k].mappingline[i] = next_int(&cp); - (*grid)[k].mappinglimits[2*i] = next_real(&cp); - (*grid)[k].mappinglimits[2*i+1] = next_real(&cp); - (*grid)[k].mappingpoints[i] = next_int(&cp); - (*grid)[k].mappingparams[i] = Rvector(0,(*grid)[k].mappingpoints[i]); - for(j=0;j<(*grid)[k].mappingpoints[i];j++) - (*grid)[k].mappingparams[i][j] = next_real(&cp); - } - if(0) printf("Loaded %d geometry mappings\n",i); - (*grid)[k].mappings = i; + if(strstr(params,"TRUE")) grid->layeredbc = 1; + if(strstr(params,"FALSE")) grid->layeredbc = 0; } + else if(strstr(command,"EXTRUDED")) { + grid->dimension = 3; - else if(strstr(command,"END") ) { - if(0) printf("End of field\n"); - } - - else if(strstr(command,"START NEW MESH")) { - if((*nogrids) >= MAXCASES) { - printf("There are more grids than was allocated for!\n"); - printf("Ignoring meshes starting from %d\n.",(*nogrids)+1); - goto end; + if(strstr(command,"EXTRUDED DIVISIONS")) { + sscanf(params,"%d",&grid->zcells); + } + if(strstr(command,"EXTRUDED BC OFFSET")) { + sscanf(params,"%d",&grid->layerbcoffset); + } + else if(strstr(command,"EXTRUDED LIMITS")) { + cp = params; + for(i=0;i<=grid->zcells;i++ ) { + grid->z[i] = next_real(&cp); + if(i > 0 && grid->z[i] < grid->z[i-1]) { + printf("Extruded limits %d: %12.6le %12.6le\n",i,grid->z[i],grid->z[i-1]); + bigerror("Values for limits should be a growing series, existing\n"); + } + } + } + else if(strstr(command,"EXTRUDED SIZES")) { + cp = params; + for(i=1;i<=grid->zcells;i++) grid->z[i] = next_real(&cp); + for(i=1;i<=grid->zcells;i++) grid->z[i] = grid->z[i-1] + grid->z[i]; + } + else if(strstr(command,"EXTRUDED ELEMENTS")) { + cp = params; + for(i=1;i<=grid->zcells;i++) grid->zelems[i] = next_int(&cp); + grid->autoratio = FALSE; + } + else if(strstr(command,"EXTRUDED RATIOS")) { + cp = params; + for(i=1;i<=grid->zcells;i++) grid->zexpand[i] = next_real(&cp); + } + else if(strstr(command,"EXTRUDED DENSITIES")) { + cp = params; + for(i=1;i<=grid->zcells;i++) grid->zdens[i] = next_real(&cp); + } + else if(strstr(command,"EXTRUDED STRUCTURE")) { + for(i=1;i<= grid->zcells;i++) { + if(i>1) Getline(params,in); + sscanf(params,"%d %d %d\n", + &grid->zfirstmaterial[i],&grid->zlastmaterial[i],&grid->zmaterial[i]); + } + } + else if(strstr(command,"EXTRUDED MAX MATERIAL")) { + sscanf(params,"%d",&grid->maxmaterial); + } + else if(strstr(command,"EXTRUDED MATERIAL MAPPINGS")) { + grid->zmaterialmap = Imatrix(1,grid->zcells,1,grid->maxmaterial); + for(i=1;i<=grid->zcells;i++) { + if(i>1) Getline(params,in); + cp = params; + for(j=1;j<=grid->maxmaterial;j++) + grid->zmaterialmap[i][j] = next_int(&cp); + } + grid->zmaterialmapexists = TRUE; + } + else if(strstr(command,"EXTRUDED HELICITY")) { + sscanf(params,"%le",&grid->zhelicity); + grid->zhelicityexists = TRUE; } - (*nogrids)++; - if(0) printf("\nLoading element meshing no %d\n",*nogrids); - k = *nogrids - 1; - if(k > nogrids0) (*grid)[k] = (*grid)[k-1]; - } - else { - if(1) printf("Unknown command: %s",command); } } end: + printf("Read commands from a file\n"); - if(0) printf("Found %d divisions for grid\n",*nogrids); - - for(k=nogrids0;k < (*nogrids) && krelh = 1.0; - eg->inmethod = 0; - eg->outmethod = 0; - eg->silent = FALSE; - eg->nofilesin = 1; - eg->unitemeshes = FALSE; - eg->triangles = FALSE; - eg->triangleangle = 0.0; - eg->rotate = FALSE; - eg->polar = FALSE; - eg->cylinder = FALSE; - eg->usenames = FALSE; - eg->layers = 0; - eg->layereps = 0.0; - eg->layermove = 0; - eg->partitions = 0; - eg->elements3d = 0; - eg->nodes3d = 0; - eg->metis = 0; - eg->partitionhalo = FALSE; - eg->partitionindirect = FALSE; - eg->reduce = FALSE; - eg->increase = FALSE; - eg->translate = FALSE; - eg->isoparam = FALSE; - eg->removelowdim = FALSE; - eg->removeunused = FALSE; - eg->dim = 3; - eg->center = FALSE; - eg->scale = FALSE; - eg->order = FALSE; - eg->boundbounds = 0; - eg->saveinterval[0] = eg->saveinterval[1] = eg->saveinterval[2] = 0; - eg->bulkbounds = 0; - eg->partorder = FALSE; - eg->findsides = FALSE; - eg->pelems = 0; - eg->belems = 0; - eg->saveboundaries = TRUE; - eg->merge = FALSE; - eg->bcoffset = FALSE; - eg->periodic = 0; - eg->periodicdim[0] = 0; - eg->periodicdim[1] = 0; - eg->periodicdim[2] = 0; - eg->bulkorder = FALSE; - eg->boundorder = FALSE; - eg->sidemappings = 0; - eg->bulkmappings = 0; - eg->clone[0] = eg->clone[1] = eg->clone[2] = 0; - eg->decimals = 12; - eg->discont = 0; - eg->connect = 0; - eg->advancedmat = 0; + hit = FALSE; + elemsides = 0; + elemtype = 0; + hit1 = FALSE; + hit2 = FALSE; + + debug = FALSE; - for(i=0;isidebulk[i] = 0; + for(parent=1;parent<=2;parent++) { + if(parent == 1) + elemind = bound->parent[sideelem]; + else + elemind = bound->parent2[sideelem]; + + if(elemind > 0) { + elemtype = data->elementtypes[elemind]; + elemsides = elemtype / 100; + + if(elemsides == 8) elemsides = 6; + else if(elemsides == 7) elemsides = 5; + else if(elemsides == 6) elemsides = 5; + else if(elemsides == 5) elemsides = 4; + + for(normal=1;normal >= -1;normal -= 2) { + + for(side=0;side 300) break; + if(sideelemtype2 < 200 && sideelemtype > 200) break; + if(sideelemtype != sideelemtype2) continue; + + sidenodes = sideelemtype / 100; + + for(j=0;jside[sideelem] = side; + bound->normal[sideelem] = normal; + } + else { + hit2 = TRUE; + bound->side2[sideelem] = side; + } + goto skip; + } + } + } + } + + + /* this finding of sides does not guarantee that normals are oriented correctly */ + normal = 1; + hit = FALSE; + + for(side=0;;side++) { + + if(0) printf("side = %d\n",side); + + GetElementSide(elemind,side,normal,data,&sideind2[0],&sideelemtype2); + + if(sideelemtype2 == 0 ) break; + if(sideelemtype2 < 300 && sideelemtype > 300) break; + if(sideelemtype2 < 200 && sideelemtype > 200) break; + if(sideelemtype != sideelemtype2) continue; + + sidenodes = sideelemtype % 100; + + nohits = 0; + for(j=0;jside[sideelem] = side; + } + else { + hit2 = TRUE; + bound->side2[sideelem] = side; + } + goto skip; + } + + } + + skip: + if(!hit) { + printf("FindParentSide: cannot locate BC element in parent %d: %d\n",parent,elemind); + printf("BC elem %d of type %d with corner indexes: ",sideelem,sideelemtype); + for(i=0;i= 5 && j<=7 ) j = j-1; + for(i=0;itopology[elemind][i]); + printf("\n"); + } + } + } + + if(hit1 || hit2) + return(0); + else + return(1); } +static int Getnamerow(char *line,FILE *io,int upper) +{ + int i,isend; + char *charend; + charend = fgets(line,MAXLINESIZE,io); + isend = (charend == NULL); + if(isend) + return(1); + else + return(0); +} -int LoadCommands(char *prefix,struct ElmergridType *eg, - struct GridType *grid, int mode,const char *IOmethods[], - int info) + + +int LoadElmerInput(struct FemType *data,struct BoundaryType *bound, + char *prefix,int nonames, int info) +/* This procedure reads the mesh assuming ElmerSolver format. + */ { - char filename[MAXFILESIZE],command[MAXLINESIZE],params[MAXLINESIZE],*cp; + int noknots,noelements,nosides,maxelemtype,maxnodes,nonodes; + int sideind[MAXNODESD1],tottypes,elementtype; + int i,j,k,l,dummyint,cdstat,fail; + int falseparents,noparents,bctopocreated; + int activeperm,activeelemperm,mini,maxi,minelem,maxelem,p1,p2; + int *nodeperm,*elemperm,*invperm,*invelemperm; + int iostat,noelements0; + FILE *in; + char line[MAXLINESIZE],line2[MAXLINESIZE],filename[MAXFILESIZE],directoryname[MAXFILESIZE]; + char *ptr1,*ptr2; - FILE *in = NULL; - int i,j; - if( mode == 0) { - if (in = fopen("ELMERGRID_STARTINFO","r")) { - fscanf(in,"%s",filename); - fclose(in); - printf("Using the file %s defined in ELMERGRID_STARTINFO\n",filename); - if ((in = fopen(filename,"r")) == NULL) { - printf("LoadCommands: opening of the file '%s' wasn't successful!\n",filename); - return(1); - } - else printf("Loading ElmerGrid commands from file '%s'.\n",filename); - } - else - return(2); + sprintf(directoryname,"%s",prefix); + cdstat = chdir(directoryname); + + if(info) { + if(cdstat) + printf("Loading mesh in ElmerSolver format from root directory.\n"); + else + printf("Loading mesh in ElmerSolver format from directory %s.\n",directoryname); } - if(mode == 1) { - AddExtension(prefix,filename,"eg"); - if ((in = fopen(filename,"r")) == NULL) { - printf("LoadCommands: opening of the file '%s' wasn't successful!\n",filename); - return(3); - } - if(info) printf("Loading ElmerGrid commands from file '%s'.\n",filename); + + InitializeKnots(data); + + + sprintf(filename,"%s","mesh.header"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadElmerInput: The opening of the header-file %s failed!\n", + filename); + return(1); } - else if(mode == 2) { - AddExtension(prefix,filename,"grd"); - if ((in = fopen(filename,"r")) == NULL) { - printf("LoadCommands: opening of the file '%s' wasn't successful!\n",filename); - return(4); - } - if(info) printf("Loading ElmerGrid commands from file '%s'.\n",filename); + else + printf("Loading header from %s\n",filename); + + GETLINE; + sscanf(line,"%d %d %d",&noknots,&noelements,&nosides); + GETLINE; + sscanf(line,"%d",&tottypes); + + maxelemtype = 0; + maxnodes = 0; + for(i=1;i<=tottypes;i++) { + GETLINE; + sscanf(line,"%d",&dummyint); + maxelemtype = MAX( dummyint, maxelemtype ); + j = maxelemtype % 100; + maxnodes = MAX( j, maxnodes ); } + printf("Maximum elementtype index is: %d\n",maxelemtype); + printf("Maximum number of nodes in element is: %d\n",maxnodes); + fclose(in); + data->dim = GetElementDimension(maxelemtype); + data->maxnodes = maxnodes; + data->noknots = noknots; + data->noelements = noelements0 = noelements; - for(;;) { - if(GetCommand(command,params,in)) { - if(0) printf("Reached the end of command file\n"); - goto end; - } + if(info) printf("Allocating for %d knots and %d elements.\n", + noknots,noelements); + AllocateKnots(data); - /* If the mode is the command file mode read also the file information from the command file. */ - if(mode <= 1) { - if(strstr(command,"INPUT FILE")) { - sscanf(params,"%s", &(eg->filesin[0])); - } + sprintf(filename,"%s","mesh.nodes"); + if ((in = fopen(filename,"r")) == NULL) { + if(info) printf("LoadElmerInput: The opening of the nodes-file %s failed!\n", + filename); + bigerror("Cannot continue without nodes file!\n"); + } + else + printf("Loading %d Elmer nodes from %s\n",noknots,filename); + + activeperm = FALSE; + for(i=1; i <= noknots; i++) { + GETLINE; + sscanf(line,"%d %d %le %le %le", + &j, &dummyint, &(data->x[i]),&(data->y[i]),&(data->z[i])); + if(j != i && !activeperm) { + printf("LoadElmerInput: The node number (%d) at node %d is not compact, creating permutation\n",j,i); + activeperm = TRUE; + nodeperm = Ivector(1,noknots); + for(k=1;kfilesout[0])); - } - else if(strstr(command,"INPUT MODE")) { - for(j=0;jinmethod = i; - break; - } - } - if(i>MAXFORMATS) sscanf(params,"%d",&eg->inmethod); + /* Create inverse permutation for nodes */ + if(activeperm) { + for(i=1;i<=noknots;i++) { + if(i==1) { + mini = nodeperm[i]; + maxi = nodeperm[i]; } - - else if(strstr(command,"OUTPUT MODE")) { - for(j=0;joutmethod = i; - break; - } - } - if(i>MAXFORMATS) sscanf(params,"%d",&eg->outmethod); + else { + mini = MIN(nodeperm[i],mini); + maxi = MAX(nodeperm[i],maxi); } - } - /* End of command file specific part */ - - - if(strstr(command,"DECIMALS")) { - sscanf(params,"%d",&eg->decimals); - } - else if(strstr(command,"TRIANGLES CRITICAL ANGLE")) { - sscanf(params,"%le",&eg->triangleangle); - } - else if(strstr(command,"TRIANGLES")) { - for(j=0;jtriangles = TRUE; - } - else if(strstr(command,"MERGE NODES")) { - eg->merge = TRUE; - sscanf(params,"%le",&eg->cmerge); - } - else if(strstr(command,"UNITE")) { - for(j=0;junitemeshes = TRUE; - } - else if(strstr(command,"ORDER NODES")) { - eg->order = TRUE; - if(eg->dim == 1) - sscanf(params,"%le",&eg->corder[0]); - else if(eg->dim == 2) - sscanf(params,"%le%le",&eg->corder[0],&eg->corder[1]); - else if(eg->dim == 3) - sscanf(params,"%le%le%le",&eg->corder[0],&eg->corder[1],&eg->corder[2]); - } - else if(strstr(command,"SCALE")) { - eg->scale = TRUE; - if(eg->dim == 1) - sscanf(params,"%le",&eg->cscale[0]); - else if(eg->dim == 2) - sscanf(params,"%le%le",&eg->cscale[0],&eg->cscale[1]); - else if(eg->dim == 3) - sscanf(params,"%le%le%le",&eg->cscale[0],&eg->cscale[1],&eg->cscale[2]); } - else if(strstr(command,"CENTRALIZE")) { - eg->center = TRUE; + if(info) printf("LoadElmerInput: Node index range is: [%d %d]\n",mini,maxi); + invperm = Ivector(mini,maxi); + for(i=mini;i<=maxi;i++) + invperm[i] = -1; + for(i=1;i<=noknots;i++) { + j = nodeperm[i]; + if( invperm[j] > 0 ) + printf("LoadElmerInput: Node %d is redundant which may be problematic!\n",j); + else + invperm[j] = i; } - else if(strstr(command,"TRANSLATE")) { - eg->translate = TRUE; - if(eg->dim == 1) - sscanf(params,"%le",&eg->ctranslate[0]); - else if(eg->dim == 2) - sscanf(params,"%le%le",&eg->ctranslate[0],&eg->ctranslate[1]); - else if(eg->dim == 3) - sscanf(params,"%le%le%le",&eg->ctranslate[0],&eg->ctranslate[1],&eg->ctranslate[2]); + } + else { + mini = 1; + maxi = noknots; + } + + + activeelemperm = FALSE; + sprintf(filename,"%s","mesh.elements"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadElmerInput: The opening of the element-file %s failed!\n", + filename); + bigerror("Cannot continue without element file!\n"); + } + else + if(info) printf("Loading %d bulk elements from %s\n",noelements,filename); + + for(i=1; i <= noelements; i++) { + iostat = fscanf(in,"%d",&j); + if(iostat <= 0 ) { + printf("LoadElmerInput: Failed reading element line %d, reducing size of element table to %d!\n",i,i-1); + data->noelements = noelements = i-1; + break; } - else if(strstr(command,"ROTATE MESH")) { - eg->rotate = TRUE; - sscanf(params,"%le%le%le",&eg->crotate[0],&eg->crotate[1],&eg->crotate[2]); + + if(i != j && !activeelemperm) { + printf("LoadElmerInput: The element numbering (%d) at element %d is not compact, creating permutation\n",j,i); + activeelemperm = TRUE; + elemperm = Ivector(1,noelements0); + for(k=1; k < i; k++) + elemperm[k] = k; + } + if( activeelemperm ) elemperm[i] = j; + iostat = fscanf(in,"%d %d",&(data->material[i]),&elementtype); + if( iostat < 2 ) { + printf("LoadElmerInput: Failed reading definitions for bulk element %d\n",j); + bigerror("Cannot continue without this data!\n"); + } + if(elementtype > maxelemtype ) { + printf("Invalid bulk elementtype: %d\n",elementtype); + bigerror("Cannot continue with invalid elements"); + } + data->elementtypes[i] = elementtype; + nonodes = elementtype % 100; + if( nonodes > maxnodes ) { + printf("Number of nodes %d in element %d is greater than allocated maximum %d\n",nonodes,j,maxnodes); + bigerror("Cannot continue with invalid elements"); + } + for(k=0;k maxi ) { + printf("Node %d in element %d is out of range: %d\n",k+1,j,l); + bigerror("Cannot continue with this node numbering"); + } + if( activeperm ) + data->topology[i][k] = invperm[l]; + else + data->topology[i][k] = l; } - else if(strstr(command,"CLONE")) { - if(strstr(command,"CLONE SIZE")) { - if(eg->dim == 1) - sscanf(params,"%le",&eg->clonesize[0]); - else if(eg->dim == 2) - sscanf(params,"%le%le",&eg->clonesize[0],&eg->clonesize[1]); - else if(eg->dim == 3) - sscanf(params,"%le%le%le",&eg->clonesize[0],&eg->clonesize[1],&eg->clonesize[2]); + } + fclose(in); + + + /* Create inverse permutation for bulk elements */ + if(activeelemperm) { + for(i=1;i<=noelements;i++) { + if(i==1) { + minelem = elemperm[i]; + maxelem = elemperm[i]; } else { - if(eg->dim == 1) - sscanf(params,"%d",&eg->clone[0]); - else if(eg->dim == 2) - sscanf(params,"%d%d",&eg->clone[0],&eg->clone[1]); - else if(eg->dim == 3) - sscanf(params,"%d%d%d",&eg->clone[0],&eg->clone[1],&eg->clone[2]); + minelem = MIN(elemperm[i],minelem); + maxelem = MAX(elemperm[i],maxelem); } } - - else if(strstr(command,"POLAR RADIUS")) { - eg->polar = TRUE; - sscanf(params,"%le",&eg->polarradius); - } - else if(strstr(command,"CYLINDER")) { - for(j=0;jcylinder = TRUE; - } - else if(strstr(command,"REDUCE DEGREE")) { - eg->reduce = TRUE; - sscanf(params,"%d%d",&eg->reducemat1,&eg->reducemat2); - } - else if(strstr(command,"INCREASE DEGREE")) { - for(j=0;jincrease = TRUE; - } - else if(strstr(command,"ADVANCED ELEMENTS")) { - printf("Loading advanced element definitions\n"); - - for(i=0;i0) Getline(params,in); - for(j=0;jadvancedelem[7*i],&eg->advancedelem[7*i+1],&eg->advancedelem[7*i+2], - &eg->advancedelem[7*i+3],&eg->advancedelem[7*i+4],&eg->advancedelem[7*i+5], - &eg->advancedelem[7*i+6]); - } - eg->advancedmat = i; - printf("Found %d definitions for advanced elements.\n",i); - } - else if(strstr(command,"POWER ELEMENTS")) { - printf("Loading p-type element definitions\n"); - - for(i=0;i0) Getline(params,in); - for(j=0;jpelemmap[3*i],&eg->pelemmap[3*i+1],&eg->pelemmap[3*i+2]); - } - eg->pelems = i; - printf("Found %d definitions for p-elements.\n",i); - } - else if(strstr(command,"BUBBLE ELEMENTS")) { - printf("Loading bubble element definitions\n"); - - for(i=0;i0) Getline(params,in); - for(j=0;jbelemmap[3*i],&eg->belemmap[3*i+1],&eg->belemmap[3*i+2]); - } - eg->belems = i; - printf("Found %d definitions for bubble elements.\n",i); - } - else if(strstr(command,"METIS OPTION")) { -#if HAVE_METIS - sscanf(params,"%d",&eg->partopt); -#else - printf("This version of ElmerGrid was compiled without Metis library!\n"); -#endif - } - else if(strstr(command,"METIS")) { -#if HAVE_METIS - sscanf(params,"%d",&eg->metis); -#else - printf("This version of ElmerGrid was compiled without Metis library!\n"); -#endif - } - else if(strstr(command,"PARTITION ORDER")) { - eg->partorder = 1; - if(eg->dim == 2) sscanf(params,"%le%le",&eg->partcorder[0],&eg->partcorder[1]); - if(eg->dim == 3) sscanf(params,"%le%le%le",&eg->partcorder[0], - &eg->partcorder[1],&eg->partcorder[2]); + if(info) printf("LoadElmerInput: Element index range is: [%d %d]\n",minelem,maxelem); + invelemperm = Ivector(minelem,maxelem); + for(i=minelem;i<=maxelem;i++) + invelemperm[i] = -1; + for(i=1;i<=noelements;i++) { + j = elemperm[i]; + if( invelemperm[j] > 0 ) + printf("LoadElmerInput: Element %d is redundant which may be problematic!\n",j); + else + invelemperm[j] = i; } - else if(strstr(command,"PARTITION")) { - if(eg->dim == 2) sscanf(params,"%d%d",&eg->partdim[0],&eg->partdim[1]); - if(eg->dim == 3) sscanf(params,"%d%d%d",&eg->partdim[0],&eg->partdim[1],&eg->partdim[2]); - eg->partitions = 1; - for(i=0;idim;i++) { - if(eg->partdim[i] < 1) eg->partdim[i] = 1; - eg->partitions *= eg->partdim[i]; - } + } + else { + minelem = 1; + maxelem = noelements; + } + + + + falseparents = 0; + noparents = 0; + bctopocreated = FALSE; + + sprintf(filename,"%s","mesh.boundary"); + if ((in = fopen(filename,"r")) == NULL) { + printf("LoadElmerInput: The opening of the boundary-file %s failed!\n", + filename); + return(4); + } + else { + if(info) printf("Loading %d boundary elements from %s\n",nosides,filename); + } + + if( nosides > 0 ) { + AllocateBoundary(bound,nosides); + data->noboundaries = 1; + }; + + i = 0; + for(k=1; k <= nosides; k++) { + + iostat = fscanf(in,"%d",&dummyint); + if( iostat < 1 ) { + printf("LoadElmerInput: Failed reading boundary element line %d, reducing size of element table to %d!\n",k,i); + bound->nosides = nosides = i; + break; + } + i++; + + iostat = fscanf(in,"%d %d %d %d",&(bound->types[i]),&p1,&p2,&elementtype); + if(iostat < 4 ) { + printf("LoadElmerInput: Failed reading definitions for boundary element %d\n",k); + bigerror("Cannot continue without this data!\n"); + } + if( p1 > 0 && (p1 < minelem || p1 > maxelem ) ) { + printf("Parent in boundary element %d out of range: %d\n",k,p1); + bigerror("Cannot continue with bad parents"); } - else if(strstr(command,"PERIODIC")) { - if(eg->dim == 2) sscanf(params,"%d%d",&eg->periodicdim[0],&eg->periodicdim[1]); - if(eg->dim == 3) sscanf(params,"%d%d%d",&eg->periodicdim[0], - &eg->periodicdim[1],&eg->periodicdim[2]); + if( p2 > 0 && (p2 < minelem || p2 > maxelem ) ) { + printf("Parent in boundary element %d out of range: %d\n",k,p2); + bigerror("Cannot continue with bad parents"); } - else if(strstr(command,"HALO")) { - for(j=0;jpartitionhalo = TRUE; + + if(activeelemperm) { + if( p1 > 0 ) p1 = invelemperm[p1]; + if( p2 > 0 ) p2 = invelemperm[p2]; } - else if(strstr(command,"INDIRECT")) { - for(j=0;jpartitionindirect = TRUE; + + if(elementtype > maxelemtype ) { + printf("Invalid boundary elementtype: %d\n",elementtype); + bigerror("Cannot continue with invalid elements"); } - else if(strstr(command,"BOUNDARY TYPE MAPPINGS")) { - for(i=0;i0) Getline(params,in); - for(j=0;jsidemap[3*i],&eg->sidemap[3*i+1],&eg->sidemap[3*i+2]); - } - printf("Found %d boundary type mappings\n",i); - eg->sidemappings = i; + nonodes = elementtype % 100; + if( nonodes > maxnodes ) { + printf("Number of nodes %d in side element %d is greater than allocated maximum %d\n",nonodes,dummyint,maxnodes); + bigerror("Cannot continue with invalid elements"); } - else if(strstr(command,"BULK TYPE MAPPINGS")) { - for(i=0;i0) Getline(params,in); - for(j=0;jbulkmap[3*i],&eg->bulkmap[3*i+1],&eg->bulkmap[3*i+2]); - } - printf("Found %d bulk type mappings\n",i); - eg->bulkmappings = i; + + for(j=0;j< nonodes ;j++) { + iostat = fscanf(in,"%d",&l); + if(activeperm) + sideind[j] = invperm[l]; + else + sideind[j] = l; + } + + if( p1 == 0 && p2 != 0 ) { + bound->parent[i] = p2; + bound->parent2[i] = p1; } - else if(strstr(command,"BOUNDARY BOUNDARY")) { - for(i=0;i0) Getline(params,in); - for(j=0;jboundbound[3*i+2],&eg->boundbound[3*i],&eg->boundbound[3*i+1]); - } - printf("Found %d boundary boundary definitions\n",i); - eg->boundbounds = i; + else { + bound->parent[i] = p1; + bound->parent2[i] = p2; } - else if(strstr(command,"MATERIAL BOUNDARY")) { - for(i=0;i0) Getline(params,in); - for(j=0;jbulkbound[3*i+2],&eg->bulkbound[3*i],&eg->bulkbound[3*i+1]); + + if(bound->parent[i] > 0) { + fail = FindParentSide(data,bound,i,elementtype,sideind); + if(fail) falseparents++; + } + else { +#if 0 + printf("Parents not specified for side %d with inds: ",dummyint); + for(j=0;j< elementtype%100 ;j++) + printf("%d ",sideind[j]); + printf("and type: %d\n",bound->types[i]); +#endif + if( !bctopocreated ) { + bound->elementtypes = Ivector(1,nosides); + for(j=1;j<=nosides;j++) + bound->elementtypes[j] = 0; + bound->topology = Imatrix(1,nosides,0,data->maxnodes-1); + bctopocreated = TRUE; } - printf("Found %d material boundary definitions\n",i); - eg->bulkbounds = i; + for(j=0;j< elementtype%100 ;j++) + bound->topology[i][j] = sideind[j]; + bound->elementtypes[i] = elementtype; + + printf("elementtype = %d %d %d\n",i,elementtype,sideind[0]); + noparents++; } + } + + if( falseparents ) { + printf("There seems to be %d false parents in the mesh\n",falseparents); + } + if( noparents ) { + printf("There seems to be %d bc elements without parents in the mesh\n",noparents); + } - else if(strstr(command,"RENUMBER BOUNDARY")) { - for(i=0;isidemap[3*i],&eg->sidemap[3*i+1],&eg->sidemap[3*i+2]); - } - printf("Found %d boundary mappings\n",i); - eg->sidemappings = i; + bound->nosides = i; + fclose(in); + + /* Save node permutation for later use */ + data->nodepermexist = activeperm; + if(activeperm) { + data->nodeperm = nodeperm; + free_Ivector(invperm,mini,maxi); + } + + /* Element permutation is irrelevant probably for practical purposes (?) and hence it is forgotten. */ + if(activeelemperm) { + free_Ivector(invelemperm,minelem,maxelem); + free_Ivector(elemperm,1,noelements0); + } + + + + sprintf(filename,"%s","mesh.names"); + if (in = fopen(filename,"r") ) { + int isbody,started,nameproblem; + + isbody = TRUE; + nameproblem = FALSE; + + if( nonames ) { + printf("Ignoring > mesh.names < because it was explicitly requested!\n"); + goto namesend; } - else if(strstr(command,"RENUMBER MATERIAL")) { - for(i=0;ibulkmap[3*i],&eg->bulkmap[3*i+1],&eg->bulkmap[3*i+2]); + + if(info) printf("Loading names for mesh parts from file %s\n",filename); + + for(;;) { + if(Getnamerow(line,in,FALSE)) goto namesend; + + if(strstr(line,"names for boundaries")) { + if(info) printf("Reading names for mesh boundaries\n"); + isbody = FALSE; + continue; + } + else if(strstr(line,"names for bodies")) { + if(info) printf("Reading names for mesh bodies\n"); + isbody = TRUE; + continue; } - printf("Found %d material mappings\n",i); - eg->bulkmappings = i; - } - else if(strstr(command,"BOUNDARY LAYER")) { - if(strstr(command,"BOUNDARY LAYER MOVE")) { - sscanf(params,"%d",&eg->layermove); + /* get position for entity name */ + ptr1 = strchr( line,'$'); + if(!ptr1) continue; + ptr1++; + + /* get position for entity index and read it */ + ptr2 = strchr( line,'='); + if(!ptr2) continue; + ptr2++; + j = next_int(&ptr2); + + /* Initialize the entity name by white spaces */ + for(i=0;ilayereps); + + /* Copy the entityname to mesh structure */ + if( isbody ) { + if(j < 0 || j > MAXBODIES ) { + printf("Cannot treat names for body %d\n",j); + nameproblem = TRUE; + } + else { + strcpy(data->bodyname[j],line2); + data->bodynamesexist = TRUE; + } } else { - for(i=0;i0) Getline(params,in); - for(j=0;jlayerbounds[i] = next_int(&cp); - eg->layernumber[i] = next_int(&cp); - eg->layerthickness[i] = next_real(&cp); - eg->layerratios[i] = next_real(&cp); - eg->layerparents[i] = next_int(&cp); + if(j < 0 || j > MAXBOUNDARIES ) { + printf("Cannot treat names for boundary %d\n",j); + nameproblem = TRUE; + } + else { + strcpy(data->boundaryname[j],line2); + data->boundarynamesexist = TRUE; } - printf("Found %d boundary layers\n",i); - eg->layers = i; } } - else if(strstr(command,"REMOVE LOWER DIMENSIONAL BOUNDARIES")) { - for(j=0;jremovelowdim = TRUE; + namesend: + + if( nameproblem ) { + data->boundarynamesexist = FALSE; + data->bodynamesexist = FALSE; + printf("Warning: omitting use of names because the indexes are beyond range, code some more...\n"); } - else if(strstr(command,"REMOVE UNUSED NODES")) { - for(j=0;jremoveunused = TRUE; + } + + + if(!cdstat) cdstat = chdir(".."); + + if(info) printf("Elmer mesh loaded successfully\n"); + + return(0); +} + + +int SaveElmerInput(struct FemType *data,struct BoundaryType *bound, + char *prefix,int decimals,int nooverwrite, int info) +/* Saves the mesh in a form that may be used as input + in Elmer calculations. + */ +{ + int noknots,noelements,material,sumsides,elemtype,fail,cdstat; + int sideelemtype,conelemtype,nodesd1,nodesd2,newtype; + int i,j,k,l,bulktypes[MAXELEMENTTYPE+1],sidetypes[MAXELEMENTTYPE+1]; + int alltypes[MAXELEMENTTYPE+1],tottypes; + int ind[MAXNODESD1],ind2[MAXNODESD1],usedbody[MAXBODIES],usedbc[MAXBCS]; + FILE *out; + char filename[MAXFILESIZE], outstyle[MAXFILESIZE]; + char directoryname[MAXFILESIZE]; + + if(!data->created) { + printf("You tried to save points that were never created.\n"); + return(1); + } + + noelements = data->noelements; + noknots = data->noknots; + sumsides = 0; + + for(i=0;i<=MAXELEMENTTYPE;i++) + alltypes[i] = bulktypes[i] = sidetypes[i] = 0; + + for(i=0;ibulkorder = TRUE; + else { + cdstat = chdir(directoryname); } - else if(strstr(command,"REORDER BOUNDARY")) { - for(j=0;jboundorder = TRUE; + } + else { + if(info) printf("Reusing an existing directory\n"); + if(nooverwrite) { + if (out = fopen("mesh.header", "r")) { + printf("Mesh seems to already exist, writing is cancelled!\n"); + return(1); + } } - else if(strstr(command,"DIMENSION")) { - sscanf(params,"%d",&eg->dim); + } + + + sprintf(filename,"%s","mesh.nodes"); + out = fopen(filename,"w"); + + if(info) printf("Saving %d coordinates to %s.\n",noknots,filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(2); + } + + sprintf(outstyle,"%%d %%d %%.%dg %%.%dg %%.%dg\n",decimals,decimals,decimals); + for(i=1; i <= noknots; i++) + fprintf(out,outstyle,i,-1,data->x[i],data->y[i],data->z[i]); + + fclose(out); + + sprintf(filename,"%s","mesh.elements"); + out = fopen(filename,"w"); + if(info) printf("Saving %d element topologies to %s.\n",noelements,filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(3); + } + + for(i=1;i<=noelements;i++) { + elemtype = data->elementtypes[i]; + material = data->material[i]; + + if(material < MAXBODIES) usedbody[material] += 1; + fprintf(out,"%d %d %d",i,material,elemtype); + + bulktypes[elemtype] += 1; + nodesd2 = elemtype%100; + for(j=0;j < nodesd2;j++) + fprintf(out," %d",data->topology[i][j]); + fprintf(out,"\n"); + } + fclose(out); + + + sprintf(filename,"%s","mesh.boundary"); + out = fopen(filename,"w"); + if(info) printf("Saving boundary elements to %s.\n",filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(4); + } + + sumsides = 0; + + + /* Save normal boundaries */ + for(j=0;j < MAXBOUNDARIES;j++) { + if(bound[j].created == FALSE) continue; + if(bound[j].nosides == 0) continue; + + for(i=1; i <= bound[j].nosides; i++) { + GetBoundaryElement(i,&bound[j],data,ind,&sideelemtype); + sumsides++; + + fprintf(out,"%d %d %d %d ", + sumsides,bound[j].types[i],bound[j].parent[i],bound[j].parent2[i]); + fprintf(out,"%d",sideelemtype); + + if(bound[j].types[i] < MAXBCS) usedbc[bound[j].types[i]] += 1; + + sidetypes[sideelemtype] += 1; + nodesd1 = sideelemtype%100; + for(l=0;lisoparam = TRUE; + } + + newtype = 0; + for(j=0;j < MAXBOUNDARIES;j++) { + if(bound[j].created == FALSE) continue; + for(i=1; i <= bound[j].nosides; i++) + newtype = MAX(newtype, bound[j].types[i]); + } + fclose(out); + + tottypes = 0; + for(i=0;i<=MAXELEMENTTYPE;i++) { + alltypes[i] = bulktypes[i] + sidetypes[i]; + if(alltypes[i]) tottypes++; + } + + sprintf(filename,"%s","mesh.header"); + out = fopen(filename,"w"); + if(info) printf("Saving header info to %s.\n",filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(4); + } + fprintf(out,"%-6d %-6d %-6d\n", + noknots,noelements,sumsides); + fprintf(out,"%-6d\n",tottypes); + for(i=0;i<=MAXELEMENTTYPE;i++) { + if(alltypes[i]) + fprintf(out,"%-6d %-6d\n",i,bulktypes[i]+sidetypes[i]); + } + fclose(out); + + + if(data->boundarynamesexist || data->bodynamesexist) { + sprintf(filename,"%s","mesh.names"); + out = fopen(filename,"w"); + if(info) printf("Saving names info to %s.\n",filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(5); } - else if(strstr(command,"NO BOUNDARY")) { - for(j=0;jsaveboundaries = FALSE; + + if(data->bodynamesexist) { + fprintf(out,"! ----- names for bodies -----\n"); + for(i=1;ibodyname[i],i); + } + if(data->boundarynamesexist) { + fprintf(out,"! ----- names for boundaries -----\n"); + for(i=1;iboundaryname[i],i); + } + fclose(out); + + sprintf(filename,"%s","entities.sif"); + out = fopen(filename,"w"); + if(info) printf("Saving entities info to %s.\n",filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(5); + } + + if(data->bodynamesexist) { + fprintf(out,"!------ Skeleton for body section -----\n"); + j = 0; + for(i=1;ibodyname[i]); + fprintf(out,"End\n\n"); + } + } } - else if(strstr(command,"EXTRUDED")) { - grid->dimension = 3; - if(strstr(command,"EXTRUDED DIVISIONS")) { - sscanf(params,"%d",&grid->zcells); - } - else if(strstr(command,"EXTRUDED LIMITS")) { - cp = params; - for(i=0;i<=grid->zcells;i++) grid->z[i] = next_real(&cp); - } - else if(strstr(command,"EXTRUDED ELEMENTS")) { - cp = params; - for(i=1;i<=grid->zcells;i++) grid->zelems[i] = next_int(&cp); - grid->autoratio = FALSE; - } - else if(strstr(command,"EXTRUDED RATIOS")) { - cp = params; - for(i=1;i<=grid->zcells;i++) grid->zexpand[i] = next_real(&cp); - } - else if(strstr(command,"EXTRUDED DENSITIES")) { - cp = params; - for(i=1;i<=grid->zcells;i++) grid->zdens[i] = next_real(&cp); - } - else if(strstr(command,"EXTRUDED STRUCTURE")) { - for(i=1;i<= grid->zcells;i++) { - if(i>1) Getline(params,in); - sscanf(params,"%d %d %d\n", - &grid->zfirstmaterial[i],&grid->zlastmaterial[i],&grid->zmaterial[i]); + if(data->boundarynamesexist) { + fprintf(out,"!------ Skeleton for boundary section -----\n"); + j = 0; + for(i=1;iboundaryname[i]); + fprintf(out,"End\n\n"); } } - } + fclose(out); } -end: - if(0) printf("Read commands from a file\n"); + if(data->nodepermexist) { + sprintf(filename,"%s","mesh.nodeperm"); + out = fopen(filename,"w"); + + if(info) printf("Saving initial node permutation to %s.\n",filename); + if(out == NULL) { + printf("opening of file was not successful\n"); + return(3); + } + for(i=1; i <= noknots; i++) + fprintf(out,"%d %d\n",i,data->nodeperm[i]); + } + + + cdstat = chdir(".."); return(0); } @@ -3272,13 +5737,9 @@ int CreateElmerGridMesh(struct GridType *grid, } } } -#if 0 - else { - CreateAllBoundaries(cell,data,boundaries,info); - } -#endif free(cell); return 0; } + diff --git a/ElmerGUI/Application/plugins/egnative.h b/ElmerGUI/Application/plugins/egnative.h index f56d84009d..0c600ecabf 100644 --- a/ElmerGUI/Application/plugins/egnative.h +++ b/ElmerGUI/Application/plugins/egnative.h @@ -1,29 +1,35 @@ -/* egnative.h */ -/* Subroutines for creating and manipulating the native format of ElmerGrid. */ - -void InitGrid(struct GridType *grid); -void CreateExampleGrid(int dim,struct GridType **grids,int *nogrids,int info); -void SetElementDivision(struct GridType *grid,Real relh,int info); -void SetCellData(struct GridType *grid,struct CellType *cell,int info); -int SetCellKnots(struct GridType *grid, struct CellType *cell,int info); -int SetCellKnots1D(struct GridType *grid, struct CellType *cell,int info); -int GetKnotCoordinate(struct CellType *cell,int i,int j,Real *x,Real *y); -int GetKnotIndex(struct CellType *cell,int i,int j); -int GetElementIndices(struct CellType *cell,int i,int j,int *ind); -int GetElementIndex(struct CellType *cell,int i,int j); -int GetElementCoordinates(struct CellType *cell,int i,int j, - Real *globalcoord,int *ind); -int GetSideInfo(struct CellType *cell,int cellno,int side,int element, - int *elemind); -void SetElementDivisionExtruded(struct GridType *grid,int info); -void SetElementDivisionCylinder(struct GridType *grid,int info); - -int SaveElmergrid(struct GridType *grid,int nogrids,char *prefix,int info); -int LoadElmergrid(struct GridType **grid,int *nogrids,char *prefix,int info); -void InitParameters(struct ElmergridType *eg); -int LoadCommands(char *prefix,struct ElmergridType *eg, - struct GridType *grid, int mode,const char *IOmethods[], - int info); -int CreateElmerGridMesh(struct GridType *grid, - struct FemType *data,struct BoundaryType *boundaries, - Real relh,int info); +/* femelmer.h + femmesh.h -> egnative.h */ +void Instructions(); +void Goodbye(); +void InitGrid(struct GridType *grid); +void CreateExampleGrid(int dim,struct GridType **grids,int *nogrids,int info); +void SetElementDivision(struct GridType *grid,Real relh,int info); +void SetCellData(struct GridType *grid,struct CellType *cell,int info); +void CreateCells(struct GridType *grid,struct CellType **cell,int info); +void DestroyCells(struct CellType **cell); +int GetKnotCoordinate(struct CellType *cell,int i,int j,Real *x,Real *y); +int GetKnotIndex(struct CellType *cell,int i,int j); +int GetElementIndices(struct CellType *cell,int i,int j,int *ind); +int GetElementIndex(struct CellType *cell,int i,int j); +int GetElementCoordinates(struct CellType *cell,int i,int j, + Real *globalcoord,int *ind); +int GetSideInfo(struct CellType *cell,int cellno,int side,int element, + int *elemind); +void SetElementDivisionExtruded(struct GridType *grid,int info); +void SetElementDivisionCylinder(struct GridType *grid,int info); + +int SaveElmergrid(struct GridType *grid,int nogrids,char *prefix,int info); +int LoadElmergrid(struct GridType **grid,int *nogrids,char *prefix,int info); + +void InitParameters(struct ElmergridType *eg); +int InlineParameters(struct ElmergridType *eg,int argc,char *argv[],int first,int info); +int LoadCommands(char *prefix,struct ElmergridType *eg, + struct GridType *grid, int mode,int info); + +int LoadElmerInput(struct FemType *data,struct BoundaryType *bound, + char *prefix,int nonames, int info); +int SaveElmerInput(struct FemType *data,struct BoundaryType *bound, + char *prefix,int decimals,int nooverwrite, int info); +int CreateElmerGridMesh(struct GridType *grid, + struct FemType *data,struct BoundaryType *boundaries, + Real relh,int info); diff --git a/ElmerGUI/Application/plugins/egtypes.h b/ElmerGUI/Application/plugins/egtypes.h index 154d1b52c5..ac3a0c8cb0 100644 --- a/ElmerGUI/Application/plugins/egtypes.h +++ b/ElmerGUI/Application/plugins/egtypes.h @@ -1,344 +1,403 @@ -/* femtypes.h */ -/* Defines the types used in the FEM model. */ - -/* Definiotins used in allocating space for the structures. */ -#define DIM 2 /* dimension of the space */ -#define MAXDOFS 20 /* maximum number of variables, e.g. T,P */ -#define MAXCELLS 100 /* maximum number of subcells in given direction */ -#define MAXBOUNDARIES 50 /* maximum number of boundaries for BCs */ -#define MAXMATERIALS 50 /* maximum index of materials */ -#define MAXCASES 12 /* maximum number of coexisting cases */ -#define MAXFILESIZE 600 /* maximum filenamesize for i/o files */ -#define MAXLINESIZE 200 /* maximum length of line to be read */ -#define MAXNAMESIZE 30 /* maximum size of the variablename */ -#define MAXPARAMS 30 /* maximum number of parameters */ -#define MAXVARS 20 /* maximum number of variables at the sides */ -#define MAXNODESD2 27 /* maximum number of 2D nodes */ -#define MAXNODESD1 9 /* maximum number of 1D nodes */ -#define MAXMAPPINGS 10 /* maximum number of geometry mappings */ -#define MAXCONNECTIONS 100 /* maximum number of connections in dual graph */ -#define MAXBCS 1000 /* maximum number of BCs in naming */ -#define MAXBODIES 100 /* maximum number of bodies in naming */ -#define MAXPARTITIONS 512 /* maximum number of partitions */ -#define MAXFORMATS 15 - -#define CONPLAIN 0 -#define CONDISCONT 1 -#define CONPERIODIC 2 -#define CONCONSTRAINT 3 - -/* Structure GridType includes the subcell structure of the - geometry and the meshing information. The elements may be - directly derived from this structures but it takes some - time and is not easy to comprehend. Therefore structures - CellType and FemType are derived from this data. The special - subcell structure is, however, utilized in some mapping - subroutines that in general cases would be much more difficult - (and expensive) to perform. - */ -struct GridType { - int dimension, - triangles, - layeredbc, - partitions, - coordsystem, /* 2D cartesian or axisymmetric? */ - layered, - autoratio, /* set the scale in x and y automatically? */ - minxelems, /* minimum number of elements */ - minyelems, - minzelems, - totxelems, /* total number of elements */ - totyelems, - totzelems, - elemorder, - elemmidpoints, - wantedelems, - limitdxverify, - firstmaterial, /* first material to be included in mesh */ - lastmaterial, /* last material to be included in mesh */ - nocells, /* number of subcells */ - xcells, /* number of subcells in x-direction */ - ycells, - zcells, - noelements, /* number of elements in the mesh */ - noknots, /* number of knots in the mesh */ - nonodes, /* number of nodes in one element */ - numbering, /* numbering scheme */ - maxwidth, /* maxwidth of the band matrix */ - noboundaries; /* number of boundaries for BCs */ - int xlinear[MAXCELLS+1], /* linearity flag within the subcells */ - ylinear[MAXCELLS+1], - zlinear[MAXCELLS+1], - xelems[MAXCELLS+1], /* number of elements within subcells */ - yelems[MAXCELLS+1], - zelems[MAXCELLS+1], - zfirstmaterial[MAXCELLS+1], - zlastmaterial[MAXCELLS+1], - zmaterial[MAXCELLS+1], - boundint[MAXBOUNDARIES], /* internal material for boundary */ - boundext[MAXBOUNDARIES], /* external material for boundary */ - boundsolid[MAXBOUNDARIES],/* which of these is the solid? */ - boundtype[MAXBOUNDARIES]; /* type of the boundary */ - int structure[MAXCELLS+2][MAXCELLS+2], /* material structure of subcells */ - numbered[MAXCELLS+2][MAXCELLS+2]; /* numbering order of the subcells */ - Real dx0, /* global mesh scale in x-direction */ - dy0, - dz0, - limitdx, - triangleangle, - xyratio, /* ratio between dx0 and dy0 */ - xzratio; - Real rotateradius1,rotateradius2,rotateimprove; - int rotate,rotateblocks,rotatecurve,rotatecartesian,mappings, - reduceordermatmin,reduceordermatmax; - Real curverad,curveangle,curvezet,polarradius; - Real x[MAXCELLS+1], /* vertical lines in the goemetry */ - y[MAXCELLS+1], /* horizontal lines in the geometry */ - z[MAXCELLS+1], - xexpand[MAXCELLS+1], /* local expand ratio in the subcells */ - yexpand[MAXCELLS+1], - zexpand[MAXCELLS+1], - xratios[MAXCELLS+1], /* relative mesh scale ratios in subcells */ - yratios[MAXCELLS+1], - zratios[MAXCELLS+1], - dx[MAXCELLS+1], /* local mesh scale in the subcells */ - dy[MAXCELLS+1], - dz[MAXCELLS+1], - xdens[MAXCELLS+1], /* local density of the mesh in the subcells */ - ydens[MAXCELLS+1], - zdens[MAXCELLS+1]; - int mappingtype[MAXMAPPINGS], - mappingline[MAXMAPPINGS], - mappingpoints[MAXMAPPINGS]; - Real mappinglimits[2*MAXMAPPINGS], - *mappingparams[MAXMAPPINGS]; -}; - -/* The elements are numbered in the program without allocating - space for the knot numbers. Only a limited number of information - for each subcell is saved to structure CellType. Specific subroutines - are then used to calculate element or knot information using this - information. Cell is one macroscopic building block that may be - divided to M x N elements. It may even consist of one element. */ -struct CellType { - int nonodes, /* number of nodes within an element */ - dimension, /* 1D or 2D */ - numbering, /* numbering scheme */ - xelem, /* number of elements in the subcell */ - yelem, - levelwidth, /* width in knot numbering */ - left1st, /* first index in the first line */ - left2nd, /* first index in the second line */ - leftlast, /* first index in the last line */ - levelwidthcenter, - leftcenter, /* first index for 8 and 9-node elements */ - left2center,/* first index in the second line of 12- and 16-node elements */ - elem1st, /* index of the lower left element */ - elemwidth, /* width in element numbering */ - xlinear, /* linearity flag */ - ylinear, - material, /* material flag */ - xind, yind; /* Indexes of the cell */ - int boundary[8], /* material indeces of neighbouring cells */ - neighbour[8]; /* number of neighbouring cells */ - Real xwidth, /* size of the subcell */ - ywidth, - xratio, /* ratio of elements in the subcell */ - yratio, - dx1, /* local mesh scale */ - dy1; - Real xcorner[4], /* coordinates of the subcell corners */ - ycorner[4]; -}; - - -/* This type includes all the element information needed for a - FEM model: the element topology, node coordinates, node indexing - and all the degrees of freedom. */ -struct FemType { - int created, /* is the structure created? */ - noknots, /* number of knots */ - noelements, /* number of elements */ - coordsystem, /* coordsystem flag */ - nocells, /* number of subcells */ - maxnodes, /* maximum number of nodes */ - dim, /* dimension of space */ - variables, /* number of variables */ - *dualgraph[MAXCONNECTIONS], - dualmaxconnections, - indexwidth, - dualexists, - - *partitiontable[MAXCONNECTIONS], - maxpartitiontable, - partitiontableexists, - - *invtopo[MAXCONNECTIONS], - maxinvtopo, - invtopoexists, - timesteps, /* number of timesteps */ - periodicexist, /* does the periodic vector exist? */ - *periodic, /* periodic ordering vector, if needed */ - connectexist, /* does the connection vector exist? */ - *connect, /* connections between nodes, if needed */ - partitionexist,/* does the partitioning exist? */ - nopartitions, /* number of partitions */ - *elempart, /* which partition owns the element */ - *nodepart, /* which partition owns the node */ - *elementtypes, /* types of elements using Elmer convention */ - *material, /* material for each element */ - **topology, /* element topology */ - bodynamesexist, - boundarynamesexist; - int edofs[MAXDOFS], /* number of dofs in each node */ - alldofs[MAXDOFS]; /* total number of variables */ - Real minsize,maxsize; - Real *x, /* in axisymmetric case r */ - *y, /* in axisymmetric case z */ - *z, /* in cylindrical case theta */ - *times; - Real *dofs[MAXDOFS]; /* degrees of freedom in the mesh */ - char dofname[MAXDOFS][MAXNAMESIZE]; - char bodyname[MAXBODIES][MAXNAMESIZE]; - char boundaryname[MAXBCS][MAXNAMESIZE]; - int noboundaries, /* number of boundaries */ - boundint[MAXBOUNDARIES], /* internal material in the boundary */ - boundext[MAXBOUNDARIES], /* external material in the boundary */ - boundsolid[MAXBOUNDARIES], /* which one is solid? */ - boundtype[MAXBOUNDARIES]; /* type of the boundary */ -}; - -/* The boundaries between different materials or domains - are saved into this structure. It is used for setting - the boundary conditions. In physics it is typical that - the BCs are more complicated than the equations in the - bulk and therefore the structure must be such that it - enables the use of a wide variety of BCs. */ -struct BoundaryType { - int created, /* is boundary created? */ - nosides, /* sides on the boundary */ - maxsidenodes, /* number of sidenodes on the element */ - fixedpoints, /* number of fixed points allowed */ - coordsystem, /* coordinate system flag */ - vfcreated, /* are view factors created */ - gfcreated, /* are Gephart factors created */ - maparea, /* mappings of the area */ - mapvf, /* mappings of the view factors */ - open, /* is the closure partially open? */ - echain, /* does the chain exist? */ - ediscont, /* does the discontinuous boundary exist */ - chainsize; /* size of the chain */ - int *parent, /* primary parents of the sides */ - *parent2, /* secondary parents of the sides */ - *material, /* material of the sides */ - *side, /* side in the primary parent element */ - *side2, /* side in the secondary parent element */ - *chain, /* indices in the chain representation */ - *types, - *discont, /* type of discontinuous and periodic BCs */ - *normal, /* direction of the normal */ - *elementtypes, /* side element types if needed */ - **topology, /* topology if needed */ - points[MAXVARS], /* how many points for each side? */ - evars[MAXVARS]; /* does the variables exist? */ - Real totalarea, /* total area of the side */ - areasexist, - *areas, /* side areas */ - **vf, /* view factors */ - **gf, /* Gephart factors */ - *vars[MAXVARS]; /* variables on the sides */ - char varname[MAXVARS][MAXNAMESIZE]; /* variable name */ -}; - - -#define MAXSIDEBULK 10 -struct ElmergridType { - - int dim, - silent, - center, - scale, /* scale the geometry */ - order, /* reorder the nodes */ - merge, /* merge mesges */ - translate, /* translate the mesh */ - rotate, /* rotate the mesh */ - clone[3], /* clone the mesh the number of given times */ - mirror[3], /* mirror the mash around the given axis */ - canter, - decimals, /* save the mesh with number of decimals */ - layers, /* create boundary layers */ - layerbounds[MAXBOUNDARIES], - layernumber[MAXBOUNDARIES], - layermove, /* map the created layer to the original geometry */ - metis, /* number of Metis partitions */ - partopt, /* free parameter for optimization */ - partitions, /* number of simple geometric partitions */ - partdim[3], - inmethod, /* method in which mesh is read in to ElmerGrid */ - outmethod, /* method in which the mesh is written by ElmerGrid */ - sidemap[3*MAXBOUNDARIES], - sidemappings, - bulkmap[3*MAXMATERIALS], - bulkmappings, - boundorder, - bulkorder, - boundbounds, - boundbound[3*MAXBOUNDARIES], - bulkbounds, - bulkbound[3*MAXBOUNDARIES], - mirrorbc, - layerparents[MAXBOUNDARIES], - sidebulk[MAXSIDEBULK], - triangles, - polar, - usenames, - isoparam, - cylinder, - info, - unitemeshes, - reduce, - removelowdim, - removeunused, - increase, - reducemat1, - reducemat2, - findsides, - saveboundaries, - nodes3d, - elements3d, - periodic, - periodicdim[3], - discont, - discontbounds[MAXBOUNDARIES], - connect, - connectbounds[MAXBOUNDARIES], - partorder, - partitionhalo, /* create halo for the partitioning */ - partitionindirect, /* should one create indirect connections between nodes */ - nofilesin, - saveinterval[3], - elementsredone, - pelemmap[4*MAXMATERIALS],pelems, - belemmap[4*MAXMATERIALS], belems, - advancedelem[7*MAXMATERIALS], advancedmat, - bcoffset; - - Real cscale[3], - corder[3], - cmerge, - ctranslate[3], - crotate[3], - clonesize[3], - layerratios[MAXBOUNDARIES], - layerthickness[MAXBOUNDARIES], - layereps, - triangleangle, - partcorder[3], - polarradius, - relh; - - char filesin[MAXCASES][MAXFILESIZE], - filesout[MAXCASES][MAXFILESIZE], - mapfile[MAXFILESIZE]; -}; - +/* femtypes.h */ +/* Defines the types used in the FEM model. */ + +/* Definiotins used in allocating space for the structures. */ +#define DIM 2 /* dimension of the space */ +#define MAXDOFS 20 /* maximum number of variables, e.g. T,P */ +#define MAXCELLS 100 /* maximum number of subcells in given direction */ +#define MAXBOUNDARIES 1000 /* maximum number of boundaries for BCs */ +#define MAXCASES 12 /* maximum number of coexisting cases */ +#define MAXFILESIZE 600 /* maximum filenamesize for i/o files */ +#define MAXLINESIZE 600 /* maximum length of line to be read */ +#define LONGLINESIZE 1201 +#define MAXNAMESIZE 30 /* maximum size of the variablename */ +#define MAXPARAMS 30 /* maximum number of parameters */ +#define MAXVARS 20 /* maximum number of variables at the sides */ +#define MAXNODESD3 64 /* maximum number of 3D nodes */ +#define MAXNODESD2 27 /* maximum number of 2D nodes */ +#define MAXNODESD1 9 /* maximum number of 1D nodes */ +#define MAXMAPPINGS 20 /* maximum number of geometry mappings */ +#define MAXCONNECTIONS 500 /* maximum number of connections in nodal or dual graph */ +#define MAXBCS 1000 /* maximum number of BCs in naming */ +#define MAXBODIES 1000 /* maximum number of bodies in naming */ +#define MAXPARTITIONS 512 /* maximum number of partitions */ +#define MAXHALOMODES 10 +#define MAXFORMATS 15 + +#define CONPLAIN 0 +#define CONDISCONT 1 +#define CONPERIODIC 2 +#define CONCONSTRAINT 3 + +#define MAXELEMENTTYPE 827 + +struct CRSType { + int *rows, *cols; + int rowsize,colsize; + int created; +}; + +/* Structure GridType includes the subcell structure of the + geometry and the meshing information. The elements may be + directly derived from this structures but it takes some + time and is not easy to comprehend. Therefore structures + CellType and FemType are derived from this data. The special + subcell structure is, however, utilized in some mapping + subroutines that in general cases would be much more difficult + (and expensive) to perform. + */ +struct GridType { + int dimension, + triangles, + layeredbc, + partitions, + coordsystem, /* 2D cartesian or axisymmetric? */ + layered, + autoratio, /* set the scale in x and y automatically? */ + minxelems, /* minimum number of elements */ + minyelems, + minzelems, + totxelems, /* total number of elements */ + totyelems, + totzelems, + elemorder, + elemmidpoints, + wantedelems, + limitdxverify, + wantedelems3d, + wantednodes3d, + firstmaterial, /* first material to be included in mesh */ + lastmaterial, /* last material to be included in mesh */ + nocells, /* number of subcells */ + xcells, /* number of subcells in x-direction */ + ycells, + zcells, + layerbcoffset, /* offset of bcs when doing extrusion */ + noelements, /* number of elements in the mesh */ + noknots, /* number of knots in the mesh */ + nonodes, /* number of nodes in one element */ + numbering, /* numbering scheme */ + maxwidth, /* maxwidth of the band matrix */ + noboundaries, /* number of boundaries for BCs */ + maxmaterial; /* maximum material index */ + int xlinear[MAXCELLS+1], /* linearity flag within the subcells */ + ylinear[MAXCELLS+1], + zlinear[MAXCELLS+1], + xelems[MAXCELLS+1], /* number of elements within subcells */ + yelems[MAXCELLS+1], + zelems[MAXCELLS+1], + zfirstmaterial[MAXCELLS+1], + zlastmaterial[MAXCELLS+1], + zmaterial[MAXCELLS+1], + boundint[MAXBOUNDARIES], /* internal material for boundary */ + boundext[MAXBOUNDARIES], /* external material for boundary */ + boundsolid[MAXBOUNDARIES],/* which of these is the solid? */ + boundtype[MAXBOUNDARIES]; /* type of the boundary */ + int **zmaterialmap,zmaterialmapexists; + Real zhelicity; + int zhelicityexists; + int structure[MAXCELLS+2][MAXCELLS+2], /* material structure of subcells */ + numbered[MAXCELLS+2][MAXCELLS+2]; /* numbering order of the subcells */ + Real dx0, /* global mesh scale in x-direction */ + dy0, + dz0, + limitdx, + triangleangle, + xyratio, /* ratio between dx0 and dy0 */ + xzratio; + Real rotateradius1,rotateradius2,rotateimprove; + int rotate,rotateblocks,rotatecurve,rotatecartesian,mappings, + reduceordermatmin,reduceordermatmax; + Real curverad,curveangle,curvezet,polarradius; + Real x[MAXCELLS+1], /* vertical lines in the goemetry */ + y[MAXCELLS+1], /* horizontal lines in the geometry */ + z[MAXCELLS+1], + xexpand[MAXCELLS+1], /* local expand ratio in the subcells */ + yexpand[MAXCELLS+1], + zexpand[MAXCELLS+1], + xratios[MAXCELLS+1], /* relative mesh scale ratios in subcells */ + yratios[MAXCELLS+1], + zratios[MAXCELLS+1], + dx[MAXCELLS+1], /* local mesh scale in the subcells */ + dy[MAXCELLS+1], + dz[MAXCELLS+1], + xdens[MAXCELLS+1], /* local density of the mesh in the subcells */ + ydens[MAXCELLS+1], + zdens[MAXCELLS+1]; + int mappingtype[MAXMAPPINGS], + mappingline[MAXMAPPINGS], + mappingpoints[MAXMAPPINGS]; + Real mappinglimits[2*MAXMAPPINGS], + *mappingparams[MAXMAPPINGS]; +}; + +/* The elements are numbered in the program without allocating + space for the knot numbers. Only a limited number of information + for each subcell is saved to structure CellType. Specific subroutines + are then used to calculate element or knot information using this + information. Cell is one macroscopic building block that may be + divided to M x N elements. It may even consist of one element. */ +struct CellType { + int nonodes, /* number of nodes within an element */ + dimension, /* 1D or 2D */ + numbering, /* numbering scheme */ + xelem, /* number of elements in the subcell */ + yelem, + levelwidth, /* width in knot numbering */ + left1st, /* first index in the first line */ + left2nd, /* first index in the second line */ + leftlast, /* first index in the last line */ + levelwidthcenter, + leftcenter, /* first index for 8 and 9-node elements */ + left2center,/* first index in the second line of 12- and 16-node elements */ + elem1st, /* index of the lower left element */ + elemwidth, /* width in element numbering */ + xlinear, /* linearity flag */ + ylinear, + material, /* material flag */ + xind, yind; /* Indexes of the cell */ + int boundary[8], /* material indeces of neighbouring cells */ + neighbour[8]; /* number of neighbouring cells */ + Real xwidth, /* size of the subcell */ + ywidth, + xratio, /* ratio of elements in the subcell */ + yratio, + dx1, /* local mesh scale */ + dy1; + Real xcorner[4], /* coordinates of the subcell corners */ + ycorner[4]; +}; + + +/* This type includes all the element information needed for a + FEM model: the element topology, node coordinates, node indexing + and all the degrees of freedom. */ +struct FemType { + int created, /* is the structure created? */ + noknots, /* number of knots */ + noelements, /* number of elements */ + nodepermexist, /* are the nodes permutated at the start */ + *nodeperm, /* Inverse node permutation to save */ + coordsystem, /* coordsystem flag */ + nocells, /* number of subcells */ + maxnodes, /* maximum number of nodes */ + dim, /* dimension of space */ + numbering, /* numbering scheme */ + variables, /* number of variables */ + indexwidth, /* maximum difference of node indices */ + mapgeo, /* mappings for geometry */ + *nodalgraph[MAXCONNECTIONS], + nodalmaxconnections, + nodalexists, + dualexists, + *partitiontable[MAXCONNECTIONS], + maxpartitiontable, + partitiontableexists, + + nocorners, /* number material corners in the mesh */ + timesteps, /* number of timesteps */ + periodicexist, /* does the periodic vector exist? */ + *periodic, /* periodic ordering vector, if needed */ + nodeconnectexist, /* does the node connection vector exist? */ + *nodeconnect, /* connections between nodes, if needed */ + elemconnectexist, /* does the element connection vector exist? */ + *elemconnect, /* connections between elements, if needed */ + partitionexist,/* does the partitioning exist? */ + nopartitions, /* number of partitions */ + *elempart, /* which partition owns the element */ + *nodepart, /* which partition owns the node */ + *corners, /* corners associated to elements */ + *elementtypes, /* types of elements if not all the same */ + *material, /* material for each element */ + **topology, /* element topology */ + bodynamesexist, + boundarynamesexist; + int edofs[MAXDOFS], /* number of dofs in each node */ + eorder[MAXDOFS], /* does order exist */ + bandwidth[MAXDOFS], /* bandwidth accounting fixed points */ + alldofs[MAXDOFS], /* total number of variables */ + iterdofs[MAXDOFS], /* iterations for variable */ + *order[MAXDOFS]; /* order of the dofs */ + Real minsize,maxsize; + Real *x, /* in axisymmetric case r */ + *y, /* in axisymmetric case z */ + *z, /* in cylindrical case theta */ + *times; + Real *dofs[MAXDOFS]; /* degrees of freedom in the mesh */ + char dofname[MAXDOFS][MAXNAMESIZE]; + char bodyname[MAXBODIES][MAXNAMESIZE]; + char boundaryname[MAXBCS][MAXNAMESIZE]; + int noboundaries, /* number of boundaries */ + boundint[MAXBOUNDARIES], /* internal material in the boundary */ + boundext[MAXBOUNDARIES], /* external material in the boundary */ + boundsolid[MAXBOUNDARIES], /* which one is solid? */ + boundtype[MAXBOUNDARIES]; /* type of the boundary */ + + struct CRSType dualgraph, /* The dual graph of the finite element mesh */ + nodalgraph2, /* The nodal graph of the finite element mesh */ + invtopo; /* The inverse of the finite element mesh topology */ +}; + +/* The boundaries between different materials or domains + are saved into this structure. It is used for setting + the boundary conditions. In physics it is typical that + the BCs are more complicated than the equations in the + bulk and therefore the structure must be such that it + enables the use of a wide variety of BCs. */ +struct BoundaryType { + int created, /* is boundary created? */ + nosides, /* sides on the boundary */ + maxsidenodes, /* number of sidenodes on the element */ + coordsystem, /* coordinate system flag */ + echain, /* does the chain exist? */ + ediscont, /* does the discontinuous boundary exist */ + chainsize; /* size of the chain */ + int *parent, /* primary parents of the sides */ + *parent2, /* secondary parents of the sides */ + *material, /* material of the sides */ + *side, /* side in the primary parent element */ + *side2, /* side in the secondary parent element */ + *chain, /* indices in the chain representation */ + *types, + *discont, /* type of discontinuous and periodic BCs */ + *normal, /* direction of the normal */ + *elementtypes, /* side element types if needed */ + **topology, /* topology if needed */ + points[MAXVARS]; /* how many points for each side? */ +}; + +/* Sometimes one point is discontinuous or there is + BC for one point only. This structure may then be + needed. */ +#define MAXNOPOINTS 20 +struct PointType { + int nopoints; + int parent[MAXNOPOINTS],corner[MAXNOPOINTS]; + int material[MAXNOPOINTS],type[MAXNOPOINTS]; +}; + + +/* Physical parameters are read with a general manner. + They may be added without constraints. */ +struct ModelType { + int iparameters, /* number of int parameters */ + rparameters, /* number of Real parameters */ + iparameter[MAXPARAMS]; /* values of int parameters */ + Real rparameter[MAXPARAMS]; /* values of Real parameters */ + char ikeyword[MAXPARAMS][MAXNAMESIZE]; /* names of int */ + char rkeyword[MAXPARAMS][MAXNAMESIZE]; /* names of Real */ +}; + + +#define MAXSIDEBULK 10 +struct ElmergridType { + + int dim, + silent, + center, + scale, /* scale the geometry */ + order, /* reorder the nodes */ + merge, /* merge mesges */ + translate, /* translate the mesh */ + rotate, /* rotate the mesh */ + clone[3], /* clone the mesh the number of given times */ + mirror[3], /* mirror the mash around the given axis */ + cloneinds, /* should the material and bc indexes be altered when cloning */ + canter, + decimals, /* save the mesh with number of decimals */ + layers, /* create boundary layers */ + layerbounds[MAXBOUNDARIES], + layernumber[MAXBOUNDARIES], + layermove, /* map the created layer to the original geometry */ + metis, /* number of Metis partitions */ + metiscontig, /* is Metis partitioning contiguous */ + metisseed, /* seed for Metis partitioning routines */ + partopt, /* free parameter for optimization */ + partoptim, /* apply aggressive optimization to node sharing on bulk */ + partbcoptim, /* apply aggressive optimization to node sharing on bcs */ + partitions, /* number of simple geometric partitions */ + partdim[3], + partjoin, /* number of parallel dimensions to be joined */ + inmethod, /* method in which mesh is read in to ElmerGrid */ + outmethod, /* method in which the mesh is written by ElmerGrid */ + sidemap[3*MAXBOUNDARIES], + sidemappings, + bulkmap[3*MAXMAPPINGS], + bulkmappings, + coordinatemap[3], + boundorder, + bulkorder, + boundbounds, + boundbound[3*MAXBOUNDARIES], + bulkbounds, + bulkbound[3*MAXBOUNDARIES], + mirrorbc, + layerparents[MAXBOUNDARIES], + sidebulk[MAXSIDEBULK], + triangles, + polar, + usenames, + isoparam, + cylinder, + unitemeshes, + reduce, + removelowdim, + removeunused, + removeintbcs, + increase, + reducemat1, + reducemat2, + findsides, + vtuone, + saveboundaries, + nodes3d, + elements3d, + periodic, + periodicdim[3], + discont, + discontbounds[MAXBOUNDARIES], + connect, + connectbounds[MAXBOUNDARIES], + connectboundsset[MAXBOUNDARIES], + connectboundsnosets, + partorder, + parthalo[MAXHALOMODES], /* create halo for the partitioning */ + partitionindirect, /* should one create indirect connections between nodes */ + partbw, /* minimize bandwidth for partitions */ + parthypre, /* renumber for hypre */ + partdual, + partbcz, + partbcr, + partbcmetis, + partbclayers, + nofilesin, + saveinterval[3], + elementsredone, + bcoffset, + rotatecurve, + timeron, + nosave, + nooverwrite, + unitenooverlap; + + Real cscale[3], + corder[3], + parttol, + cmerge, + ctranslate[3], + crotate[3], + clonesize[3], + layerratios[MAXBOUNDARIES], + layerthickness[MAXBOUNDARIES], + layereps, + triangleangle, + partcorder[3], + polarradius, + curverad,curveangle,curvezet, + relh; + + char filesin[MAXCASES][MAXFILESIZE], + filesout[MAXCASES][MAXFILESIZE], + infofile[MAXFILESIZE]; +}; + diff --git a/ElmerGUI/Application/plugins/egutils.cpp b/ElmerGUI/Application/plugins/egutils.cpp index 4f8e71fa3f..1a61d99a1e 100644 --- a/ElmerGUI/Application/plugins/egutils.cpp +++ b/ElmerGUI/Application/plugins/egutils.cpp @@ -23,18 +23,21 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - -/* -------------------------------: egutils.c :---------------------------- - Includes common operations for operating vectors and such. -*/ - -#include #include +#include #include +#include #include +#include +#include -#include "egutils.h" +/* Possible monitoring of memory usage, if supported */ +#define MEM_USAGE 0 +#if MEM_USAGE +#include +#endif +#include "egutils.h" #define FREE_ARG char* #define SHOWMEM 0 @@ -52,6 +55,17 @@ int MemoryUsage() #endif +/* The following routines are copied from the book + "Numerical Recipes in C, The art of scientific computing" + by Cambridge University Press and include the following + + Non-Copyright Notice: This appendix and its utility routines are + herewith placed into the public domain. Anyone may copy them freely + for any purpose. We of course accept no liability whatsoever for + any such use. */ + + + void nrerror(const char error_text[]) /* standerd error handler */ { @@ -63,6 +77,7 @@ void nrerror(const char error_text[]) /* Vector initialization */ + float *vector(int nl,int nh) /* allocate a float vector with subscript range v[nl..nh] */ { @@ -80,6 +95,11 @@ int *ivector(int nl,int nh) { int *v; + if( nh < nl ) { + printf("Allocation impossible in ivector: %d %d\n",nl,nh); + exit(1); + } + v=(int*) malloc((size_t) (nh-nl+1+1)*sizeof(int)); if (!v) nrerror("allocation failure in ivector()"); @@ -122,6 +142,11 @@ double *dvector(int nl,int nh) { double *v; + if( nh < nl ) { + printf("Allocation impossible in dvector: %d %d\n",nl,nh); + exit(1); + } + v=(double *)malloc((size_t) (nh-nl+1+1)*sizeof(double)); if (!v) nrerror("allocation failure in dvector()"); @@ -168,6 +193,13 @@ double **dmatrix(int nrl,int nrh,int ncl,int nch) { int i, nrow=nrh-nrl+1, ncol=nch-ncl+1; double **m; + + if( nrh < nrl || nch < ncl ) { + printf("Allocation impossible in dmatrix: %d %d %d %d\n",nrl,nrh,ncl,nch); + exit(1); + } + + /* allocate pointers to rows */ m=(double **) malloc((size_t) (nrow+1)*sizeof(double*)); @@ -201,6 +233,11 @@ int **imatrix(int nrl,int nrh,int ncl,int nch) { int i, nrow=nrh-nrl+1, ncol=nch-ncl+1; int **m; + + if( nrh < nrl || nch < ncl ) { + printf("Allocation impossible in imatrix: %d %d %d %d\n",nrl,nrh,ncl,nch); + exit(1); + } /* allocate pointers to rows */ m=(int **) malloc((size_t) (nrow+1)*sizeof(int*)); @@ -228,6 +265,59 @@ int **imatrix(int nrl,int nrh,int ncl,int nch) } + +float **submatrix(float **a,int oldrl,int oldrh,int oldcl,int oldch,int newrl,int newcl) +/* point a submatrix [newrl..][newcl..] to a[oldrl..oldrh][oldcl..oldch] */ +{ + int i,j, nrow=oldrh-oldrl+1, ncol=oldcl-newcl; + float **m; + + /* allocate array of pointers to rows */ + m=(float **) malloc((size_t) ((nrow+1)*sizeof(float*))); + if (!m) nrerror("allocation failure in submatrix()"); + m += 1; + m -= newrl; + + /* set pointers to rows */ + for(i=oldrl,j=newrl;i<=oldrh;i++,j++) + m[j]=a[i]+ncol; + + return(m); +} + +/* Tensor initialization */ + +double ***f3tensor(int nrl,int nrh,int ncl,int nch,int ndl,int ndh) +/* allocate a double 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */ +{ + int i,j,nrow=nrh-nrl+1,ncol=nch-ncl+1,ndep=ndh-ndl+1; + double ***t; + + t=(double***) malloc((size_t)((nrow+1)*sizeof(double***))); + if (!t) nrerror("allocation failure 1 in f3tensor()"); + t += 1; + t -= nrl; + + t[nrl]=(double**) malloc((size_t)((nrow*ncol+1)*sizeof(double*))); + if(!t[nrl]) nrerror("allocation failure 2 in f3tensor()"); + t[nrl] += 1; + t[nrl] -= ncl; + + t[nrl][ncl]=(double*) malloc((size_t)((nrow*ncol*ndep+1)*sizeof(double))); + if(!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()"); + t[nrl][ncl] += 1; + t[nrl][ncl] -= ndl; + + for(j=ncl+1;j<=nch;j++) t[nrl][j] = t[nrl][j-1]+ndep; + for(i=nrl+1;i<=nrh;i++) { + t[i] = t[i-1]+ncol; + t[i][ncl] = t[i-1][ncl]+ncol*ndep; + for(j=ncl+1;j<=nch;j++) t[i][j] = t[i][j-1]+ndep; + } + return(t); +} + + /* Deallocation routines */ void free_vector(float *v,int nl,int nh) @@ -242,7 +332,7 @@ void free_ivector(int *v,int nl,int nh) #endif free((FREE_ARG) (v+nl-1)); -} +} void free_cvector(unsigned char *v,int nl,int nh) { @@ -294,6 +384,104 @@ void free_imatrix(int **m,int nrl,int nrh,int ncl,int nch) free((FREE_ARG) (m+nrl-1)); } +void free_submatrix(float **b,int nrl,int nrh,int ncl,int nch) +{ + free((FREE_ARG) (b+nrl-1)); +} + +void free_f3tensor(double ***t,int nrl,int nrh,int ncl,int nch,int ndl,int ndh) +{ + free((FREE_ARG) (t[nrl][ncl]+ndl-1)); + free((FREE_ARG) (t[nrl]+ncl-1)); + free((FREE_ARG) (t+nrl-1)); +} + + +/* -------------------------------: common.c :---------------------------- + Includes common operations for operating vectors and such. */ + + +static Real timer_t0, timer_dt; +static int timer_active = FALSE; +static char timer_filename[600]; + +void timer_init() +{ + timer_active = FALSE; +} + + +void timer_activate(const char *prefix) +{ + Real time; + timer_active = TRUE; + + time = clock() / (double)CLOCKS_PER_SEC; + + AddExtension(prefix,timer_filename,"time"); + + printf("Activating timer (s): %.2f\n",time); + printf("Saving timer info to file %s\n",timer_filename); + + timer_dt = time; + timer_t0 = time; +} + + +void timer_show() +{ + static int visited = 0; + Real time; + FILE *out; +#if MEM_USAGE + int who,ret; + Real memusage; + static struct rusage usage; +#endif + + if(!timer_active) return; + + time = clock() / (double)CLOCKS_PER_SEC; + printf("Elapsed time (s): %.2f %.2f\n",time-timer_t0,time-timer_dt); + +#if MEM_USAGE + who = RUSAGE_SELF; + ret = getrusage( who, &usage ); + if( !ret ) { + printf("maxrss %ld\n",usage.ru_maxrss); + printf("ixrss %ld\n",usage.ru_ixrss); + printf("idrss %ld\n",usage.ru_idrss); + printf("isrss %ld\n",usage.ru_isrss); + + memusage = (double) 1.0 * usage.ru_maxrss; + } + else { + printf("Failed to obtain resource usage!\n"); + memusage = 0.0; + } +#endif + + visited = visited + 1; + if( visited == 1 ) { + out = fopen(timer_filename,"w"); + } + else { + out = fopen(timer_filename,"a"); + } + +#if MEM_USAGE + fprintf(out,"%3d %12.4le %12.4le %12.4le\n",visited,time-timer_t0,time-timer_dt,memusage); +#else + fprintf(out,"%3d %12.4le %12.4le\n",visited,time-timer_t0,time-timer_dt); +#endif + + fclose(out); + + timer_dt = time; +} + + + void bigerror(const char error_text[]) { @@ -309,7 +497,6 @@ void smallerror(const char error_text[]) fprintf(stderr,"The program encountered a minor error...\n"); fprintf(stderr,"%s\n",error_text); fprintf(stderr,"...we'll try to continue...\n"); - exit(1); } @@ -345,8 +532,9 @@ int Minimi(Real *vector,int first,int last) /* Returns the position of the smallest value of vector in range [first,last]. */ { Real min; - int i,mini = 0; + int i,mini; + mini=first; min=vector[first]; for(i=first+1;i<=last;i++) if(min>vector[i]) @@ -374,8 +562,9 @@ int Maximi(Real *vector,int first,int last) /* Returns the position of the largest value of vector in range [first,last]. */ { Real max; - int i,maxi = 0; + int i,maxi; + maxi=-1; max=vector[first]; for(i=first+1;i<=last;i++) if(max ptr1) badpoint = TRUE; + if(!badpoint) *ptr1 = '\0'; + } strcat(fname2, "."); strcat(fname2,newext); } -int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) + +int StringToStrings(const char *buf,char args[10][15],int maxcnt,char separator) /* Finds real numbers separated by a certain separator from a string. 'buf' - input string ending to a EOF 'dest' - a vector of real numbers @@ -429,7 +622,7 @@ int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) do { ptr2 = strchr(ptr1,separator); if(ptr2) { - for(i=0;i<10;i++) { + for(i=0;i<15;i++) { args[cnt][i] = ptr1[i]; if(ptr1 + i >= ptr2) break; } @@ -437,7 +630,7 @@ int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) ptr1 = ptr2+1; } else { - for(i=0;i<10;i++) { + for(i=0;i<15;i++) { if(ptr1 + i >= buf+totlen) break; args[cnt][i] = ptr1[i]; } @@ -480,12 +673,40 @@ int StringToInteger(const char *buf,int *dest,int maxcnt,char separator) { int cnt = 0; char *ptr1 = (char *)buf, *ptr2; + int ival; + + if (!buf[0]) return 0; + do { + + ptr2 = strchr(ptr1,separator); + if (ptr2) ptr2[0] = '\0'; + ival = atoi(ptr1); + + dest[cnt++] = ival; + + if (ptr2) ptr1 = ptr2+1; + } while (cnt < maxcnt && ptr2 != NULL); + + return cnt; +} +int StringToIntegerNoZero(const char *buf,int *dest,int maxcnt,char separator) +{ + int cnt = 0; + char *ptr1 = (char *)buf, *ptr2; + int ival; + if (!buf[0]) return 0; do { + ptr2 = strchr(ptr1,separator); if (ptr2) ptr2[0] = '\0'; - dest[cnt++] = atoi(ptr1); + ival = atoi(ptr1); + + if(ival == 0) break; + + dest[cnt++] = ival; + if (ptr2) ptr1 = ptr2+1; } while (cnt < maxcnt && ptr2 != NULL); @@ -503,6 +724,18 @@ int next_int(char **start) return(i); } +int next_int_n(char **start, int n) +{ + int i; + char *end = *start+n; + char saved = *end; + + *end = '\0'; + i = strtol(*start,NULL,10); + *end = saved; + *start = end; + return(i); +} Real next_real(char **start) { @@ -516,81 +749,74 @@ Real next_real(char **start) } - -/* Indexing algorithm, Creates an index table */ -#define SWAPI(a,b) itemp=(a);(a)=(b);(b)=itemp; -#define M 7 -#define NSTACK 50 - -void SortIndex(int n,double *arr,int *indx) +/* + * sort: sort an (double) array to ascending order, and move the elements of + * another (integer) array accordingly. the latter can be used as track + * keeper of where an element in the sorted order at position (k) was in + * in the original order (Ord[k]), if it is initialized to contain + * numbers (0..N-1) before calling sort. + * + * Parameters: + * + * N: int / number of entries in the arrays. + * Key: double[N] / array to be sorted. + * Ord: int[N] / change this accordingly. + */ +void SortIndex( int N, double *Key, int *Ord ) { - int i,indxt,ir,itemp,j,k,l; - int jstack,*istack; - double a; + double CurrentKey; - ir = n; - l = 1; - jstack = 0; - istack = ivector(1,NSTACK); + int CurrentOrd; - for(j=1;j<=n;j++) - indx[j] = j; + int CurLastPos; + int CurHalfPos; - for(;;) { - if (ir-l < M) { - for(j=l+1;j<=ir;j++) { - indxt = indx[j]; - a = arr[indxt]; - for(i=j-1;i>=1;i--) { - if(arr[indx[i]] <= a) break; - indx[i+1] = indx[i]; - } - indx[i+1] = indxt; - } - if(jstack == 0) break; - ir = istack[jstack--]; - l = istack[jstack--]; - } - else { - k = (l+ir) >> 1; - SWAPI(indx[k],indx[l+1]); - if(arr[indx[l+1]] > arr[indx[ir]]) { - SWAPI(indx[l+1],indx[ir]); - } - if(arr[indx[l]] > arr[indx[ir]]) { - SWAPI(indx[l],indx[ir]); - } - if(arr[indx[l+1]] > arr[indx[l]]) { - SWAPI(indx[l+1],indx[l]); + int i; + int j; + + /* Initialize order */ + for(i=1;i<=N;i++) + Ord[i] = i; + + CurHalfPos = N / 2 + 1; + CurLastPos = N; + while( 1 ) { + if ( CurHalfPos > 1 ) { + CurHalfPos--; + CurrentKey = Key[CurHalfPos]; + CurrentOrd = Ord[CurHalfPos]; + } else { + CurrentKey = Key[CurLastPos]; + CurrentOrd = Ord[CurLastPos]; + Key[CurLastPos] = Key[1]; + Ord[CurLastPos] = Ord[1]; + CurLastPos--; + if ( CurLastPos == 1 ) { + Key[1] = CurrentKey; + Ord[1] = CurrentOrd; + return; } - i = l+1; - j = ir; - indxt = indx[l]; - a = arr[indxt]; - for(;;) { - do i++; while(arr[indx[i]] < a); - do j--; while(arr[indx[j]] > a); - if(j < i) break; - SWAPI(indx[i],indx[j]); + } + i = CurHalfPos; + j = 2 * CurHalfPos; + while( j <= CurLastPos ) { + if ( j < CurLastPos && Key[j] < Key[j + 1] ) { + j++; } - indx[l] = indx[j]; - indx[j] = indxt; - jstack += 2; - if(jstack > NSTACK) printf("NSTACK too small in SortIndex."); - if(ir-i+1 >= j-l) { - istack[jstack] = ir; - istack[jstack-1] = i; - ir = j-1; + if ( CurrentKey < Key[j] ) { + Key[i] = Key[j]; + Ord[i] = Ord[j]; + i = j; + j += i; } else { - istack[jstack] = j-1; - istack[jstack-1] = l; - l = i; + j = CurLastPos + 1; } } + Key[i] = CurrentKey; + Ord[i] = CurrentOrd; } - free_ivector(istack,1,NSTACK); -} +} diff --git a/ElmerGUI/Application/plugins/egutils.h b/ElmerGUI/Application/plugins/egutils.h index b5422f8a22..fe4f6656a3 100644 --- a/ElmerGUI/Application/plugins/egutils.h +++ b/ElmerGUI/Application/plugins/egutils.h @@ -1,61 +1,75 @@ -/* common.h */ -/* Common subroutines that operate on vectors, matrices and other basic - data types: Find the minimum or maximum place or value, find - the mean, calculate the mean difference, save to or load from - an external file etc. */ - -#ifndef _COMMON_H_ -#define _COMMON_H_ - -typedef double Real; -#define Rvector dvector -#define Ivector ivector -#define Rmatrix dmatrix -#define Imatrix imatrix -#define free_Rvector free_dvector -#define free_Ivector free_ivector -#define free_Rmatrix free_dmatrix -#define free_Imatrix free_imatrix -#define TRUE 1 -#define FALSE 0 - -/* Numerical Recipes' uncopyrighted vector and matrix allocation - and deallocation routines. */ -int MemoryUsage(); -void nrerror(const char error_text[]); - -float *vector(int,int); -int *ivector(int,int); -unsigned char *cvector(int,int); -unsigned long *lvector(int,int); -double *dvector(int,int); - -float **matrix(int,int,int,int); -double **dmatrix(int,int,int,int); -int **imatrix(int,int,int,int); - -void free_vector(float *,int,int); -void free_ivector(int *,int,int); -void free_cvector(unsigned char *,int,int); -void free_lvector(unsigned long *,int,int); -void free_dvector(double *,int,int); - -void free_matrix(float **,int,int,int,int); -void free_dmatrix(double **,int,int,int,int); -void free_imatrix(int **,int,int,int,int); - -void bigerror(const char error_text[]); -void smallerror(const char error_text[]); -int FileExists(char *filename); -Real Minimum(Real *vector,int first,int last); -int Minimi(Real *vector,int first,int last); -Real Maximum(Real *vector,int first,int last); -int Maximi(Real *vector,int first,int last); -void AddExtension(const char *fname1,char *fname2,const char *newext); -int StringToStrings(const char *buf,char argv[10][10],int argc,char separator); -int StringToReal(const char *buf,Real *dest,int maxcnt,char separator); -int StringToInteger(const char *buf,int *dest,int maxcnt,char separator); -int next_int(char **start); -Real next_real(char **start); -void SortIndex(int n,double *arr,int *indx); -#endif +/* nrutil.h + common.h -> egutils.h */ + + +#ifndef _EGUTILS_H_ +#define _EGUTILS_H_ + + +typedef double Real; +#define Rvector dvector +#define Ivector ivector +#define Rmatrix dmatrix +#define Imatrix imatrix +#define free_Rvector free_dvector +#define free_Ivector free_ivector +#define free_Rmatrix free_dmatrix +#define free_Imatrix free_imatrix +#define TRUE 1 +#define FALSE 0 + +/* Numerical Recipes' uncopyrighted vector and matrix allocation + and deallocation routines. */ +int MemoryUsage(); + +void nrerror(const char error_text[]); + +float *vector(int,int); +int *ivector(int,int); +unsigned char *cvector(int,int); +unsigned long *lvector(int,int); +double *dvector(int,int); + +float **matrix(int,int,int,int); +double **dmatrix(int,int,int,int); +int **imatrix(int,int,int,int); +float **submatrix(float **,int,int,int,int,int,int); +double ***f3tensor(int nrl,int nrh,int ncl,int nch,int ndl,int ndh); + +void free_vector(float *,int,int); +void free_ivector(int *,int,int); +void free_cvector(unsigned char *,int,int); +void free_lvector(unsigned long *,int,int); +void free_dvector(double *,int,int); + +void free_matrix(float **,int,int,int,int); +void free_dmatrix(double **,int,int,int,int); +void free_imatrix(int **,int,int,int,int); +void free_submatrix(float **,int,int,int,int); +void free_f3tensor(double ***t,int nrl,int nrh,int ncl,int nch,int ndl,int ndh); + +/* Common subroutines that operate on vectors, matrices and other basic + data types: Find the minimum or maximum place or value, find + the mean, calculate the mean difference, save to or load from + an external file etc. */ + +void timer_init(); +void timer_activate(const char *prefix); +void timer_show(); + +void bigerror(const char error_text[]); +void smallerror(const char error_text[]); +int FileExists(char *filename); +Real Minimum(Real *vector,int first,int last); +int Minimi(Real *vector,int first,int last); +Real Maximum(Real *vector,int first,int last); +int Maximi(Real *vector,int first,int last); +void AddExtension(const char *fname1,char *fname2,const char *newext); +int StringToStrings(const char *buf,char argv[10][15],int argc,char separator); +int StringToReal(const char *buf,Real *dest,int maxcnt,char separator); +int StringToInteger(const char *buf,int *dest,int maxcnt,char separator); +int StringToIntegerNoZero(const char *buf,int *dest,int maxcnt,char separator); +int next_int(char **start); +int next_int_n(char **start, int n); +Real next_real(char **start); +void SortIndex( int N, double *Key, int *Ord ); +#endif diff --git a/ElmerGUI/Application/plugins/grid2gui.sh b/ElmerGUI/Application/plugins/grid2gui.sh new file mode 100755 index 0000000000..b948c5489f --- /dev/null +++ b/ElmerGUI/Application/plugins/grid2gui.sh @@ -0,0 +1,10 @@ +#!/bin/bash +cp -f ../../../elmergrid/src/egconvert.c egconvert.cpp +cp -f ../../../elmergrid/src/egmesh.c egmesh.cpp +cp -f ../../../elmergrid/src/egnative.c egnative.cpp +cp -f ../../../elmergrid/src/egutils.c egutils.cpp +cp -f ../../../elmergrid/src/egconvert.h . +cp -f ../../../elmergrid/src/egmesh.h . +cp -f ../../../elmergrid/src/egnative.h . +cp -f ../../../elmergrid/src/egutils.h . +cp -f ../../../elmergrid/src/egtypes.h . diff --git a/ElmerGUI/Application/src/meshcontrol.cpp b/ElmerGUI/Application/src/meshcontrol.cpp index 5169f68314..b47a0fdc0b 100755 --- a/ElmerGUI/Application/src/meshcontrol.cpp +++ b/ElmerGUI/Application/src/meshcontrol.cpp @@ -162,7 +162,7 @@ void MeshControl::defaultControls() ui.nglibMaxHEdit->setText("1000000"); ui.nglibFinenessEdit->setText("0.5"); ui.nglibBgmeshEdit->setText(""); - ui.elmerGridStringEdit->setText("-relh 1.0"); + ui.elmerGridStringEdit->setText("-autoclean -relh 1.0"); ui.elementCodesStringEdit->setText(""); } diff --git a/ElmerGUI/Application/vtkpost/matc.cpp b/ElmerGUI/Application/vtkpost/matc.cpp index dbbe6ad061..fd6b367399 100755 --- a/ElmerGUI/Application/vtkpost/matc.cpp +++ b/ElmerGUI/Application/vtkpost/matc.cpp @@ -50,6 +50,12 @@ #include #include +#ifndef FALSE +#define FALSE 0 +#endif + + + using namespace std; Matc::Matc(QWidget *parent) diff --git a/ElmerGUIlogger/CMakeLists.txt b/ElmerGUIlogger/CMakeLists.txt index f31389e455..55ee453ca5 100644 --- a/ElmerGUIlogger/CMakeLists.txt +++ b/ElmerGUIlogger/CMakeLists.txt @@ -2,16 +2,22 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules") MESSAGE(STATUS "------------------------------------------------") +IF(ElmerGUIloggerSTANDALONE) + PROJECT(ElmerGUIlogger CXX C) + IF(WIN32) + INCLUDE(cmake/windows_bundle.cmake) + ENDIF(WIN32) +ENDIF(ElmerGUIloggerSTANDALONE) IF(WITH_QT5) MESSAGE(STATUS "------------------------------------------------") - SET(QT5_PKG_LIST Qt5OpenGL Qt5Xml Qt5Script Qt5Gui Qt5Core Qt5PrintSupport) + SET(QT5_PKG_LIST Qt5OpenGL Qt5Xml Qt5Script Qt5Gui Qt5Core Qt5Widgets Qt5PrintSupport) FOREACH(_pkg ${QT5_PKG_LIST}) FIND_PACKAGE(${_pkg} PATHS ${QT5_PATH}) ENDFOREACH() ADD_DEFINITIONS(-DWITH_QT5) MESSAGE(STATUS " [ElmerGUIlogger] Qt5: " ${Qt5_FOUND}) - MESSAGE(STATUS " [ElmerGUIlogger] Qt5 Libraries: ${Qt5OpenGL_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Script_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES}") + MESSAGE(STATUS " [ElmerGUIlogger] Qt5 Libraries: ${Qt5OpenGL_LIBRARIES} ${Qt5Xml_LIBRARIES} ${Qt5Script_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Core_LIBRARIES} ${Qt5PrintSupport_LIBRARIES}") MESSAGE(STATUS "------------------------------------------------") ELSE() MESSAGE(STATUS "------------------------------------------------") @@ -21,15 +27,6 @@ ELSE() MESSAGE(STATUS "------------------------------------------------") ENDIF() - -IF(ElmerGUIloggerSTANDALONE) - PROJECT(ElmerGUIlogger CXX C) - IF(WIN32) - INCLUDE(cmake/windows_bundle.cmake) - ENDIF(WIN32) -ENDIF(ElmerGUIloggerSTANDALONE) - - SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTORCC ON) @@ -63,6 +60,11 @@ ENDIF() ADD_EXECUTABLE(ElmerGUIlogger WIN32 ${SOURCES} ${UI_RESOURCES}) + +IF(WITH_QT5) + QT5_USE_MODULES(ElmerGUIlogger OpenGL Xml Script Gui Core Widgets PrintSupport) +ENDIF() + TARGET_LINK_LIBRARIES(ElmerGUIlogger ${QT_LIBRARIES}) INSTALL(TARGETS ElmerGUIlogger RUNTIME DESTINATION "bin" COMPONENT "elmergui") diff --git a/ElmerGUItester/CMakeLists.txt b/ElmerGUItester/CMakeLists.txt index ad7c1e5c3f..5599fcc973 100644 --- a/ElmerGUItester/CMakeLists.txt +++ b/ElmerGUItester/CMakeLists.txt @@ -4,7 +4,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) IF(WITH_QT5) MESSAGE(STATUS "------------------------------------------------") - SET(QT5_PKG_LIST Qt5Gui Qt5Core) + SET(QT5_PKG_LIST Qt5Gui Qt5Core Qt5Widgets) FOREACH(_pkg ${QT5_PKG_LIST}) FIND_PACKAGE(${_pkg} PATHS ${QT5_PATH}) ENDFOREACH() @@ -16,7 +16,7 @@ ENDIF() SET(CMAKE_INCLUDE_CURRENT_DIR ON) SET(CMAKE_AUTOMOC ON) SET(CMAKE_AUTORCC ON) -SET(CMAKE_AUTOUIC ON) +SET(CMAKE_AUTOUIC OFF) SET(TARGETS ElmerGUItester) @@ -48,5 +48,10 @@ ENDIF() INCLUDE_DIRECTORIES(${APPLICATION_INCLUDE_DIRS}) ADD_EXECUTABLE(ElmerGUItester WIN32 ${SOURCES} ${UI_HEADERS} ${UI_RESOURCES}) + +IF(WITH_QT5) + QT5_USE_MODULES(ElmerGUItester Gui Core Widgets) +ENDIF() + TARGET_LINK_LIBRARIES(ElmerGUItester ${QT_LIBRARIES}) INSTALL(TARGETS ElmerGUItester RUNTIME DESTINATION "bin" COMPONENT "elmergui") diff --git a/README.adoc b/README.adoc index 3a756b9226..2eb735d333 100644 --- a/README.adoc +++ b/README.adoc @@ -28,31 +28,31 @@ Elmer is a finite element software for numerical solution of partial differentia Elmer consists of several parts. The most important ones are ElmerSolver the finite element solver, ElmerGUI the graphical user interface, and ElmerGrid the mesh creation and manipulation tool. Also a visualization tool, ElmerPost, is included in the package but it is no longer developed. -=== Download Binaries: +=== Download binaries -You may download binaries and virtual machines from http://www.elmerfem.org/blog/binaries/[here] +You may download binaries and virtual machines from http://www.elmerfem.org/blog/binaries/[here]. -=== Docker: +=== Docker - * nwrichmond/elmerice: https://hub.docker.com/r/nwrichmond/elmerice/[Docker Hub], More info https://raw.githubusercontent.com/ElmerCSC/elmerfem/release/ReleaseNotes/release_8.4.txt[here] + * nwrichmond/elmerice: https://hub.docker.com/r/nwrichmond/elmerice/[Docker Hub], https://raw.githubusercontent.com/ElmerCSC/elmerfem/release/ReleaseNotes/release_8.4.txt[more info] * unifem/Elmer-desktop: https://github.com/unifem/Elmer-desktop[GitHub] - * CoSci-LLC/docker-elmerice https://hub.docker.com/repository/docker/coscillc/elmerice[Docker Hub], https://github.com/CoSci-LLC/docker-elmerice[GitHub] + * CoSci-LLC/docker-elmerice: https://hub.docker.com/repository/docker/coscillc/elmerice[Docker Hub], https://github.com/CoSci-LLC/docker-elmerice[GitHub] === Documentation -You may find the PDFs for the documentation http://www.elmerfem.org/blog/documentation/[here] +You may find the PDFs for the documentation http://www.elmerfem.org/blog/documentation/[here]. -=== Compiling: +=== Compiling -==== macOS: +==== macOS - * download this repository either az a zip file via GitHub or using `git clone https://github.com/ElmerCSC/elmerfem.git` - * go to the downloaded directory `mkdir build` and `cd build` - * Install HomeBrew - * Install GNU GCC `brew install gcc` + * Download this repository either az a zip file via GitHub or using `git clone https://github.com/ElmerCSC/elmerfem.git` + * Go to the downloaded directory `mkdir build` and `cd build` + * Install Homebrew + * Install GCC `brew install gcc` * Install CMake `brew install cmake` - * Without: + * Without MPI: ** `cmake .. -D WITH_OpenMP:BOOLEAN=TRUE` * With MPI: ** Install OpenMPI `brew install open-mpi` @@ -69,8 +69,9 @@ You may find the PDFs for the documentation http://www.elmerfem.org/blog/documen ==== Ubuntu - * install the dependencies `sudo apt install git cmake build-essential fortran libopenmpi-dev libblas-dev liblapack-dev` - * Without: + * Download the source code and create `build` directory as above + * Install the dependencies `sudo apt install git cmake build-essential gfortran libopenmpi-dev libblas-dev liblapack-dev` + * Without MPI: ** `cmake .. -DWITH_OpenMP:BOOLEAN=TRUE` * With MPI: ** `cmake .. -DWITH_OpenMP:BOOLEAN=TRUE -DWITH_MPI:BOOLEAN=TRUE` @@ -79,25 +80,28 @@ You may find the PDFs for the documentation http://www.elmerfem.org/blog/documen ** `cmake .. -DWITH_OpenMP:BOOLEAN=TRUE -DWITH_MPI:BOOLEAN=TRUE -DWITH_ELMERGUI:BOOLEAN=TRUE` * `make` * `sudo make install` - * the executable are in `/usr/local/bin` folder, you may add this to your PATH if you will. + * The executables are in `/usr/local/bin` folder, you may add this to your PATH if you will -=== Licencing: image:https://img.shields.io/badge/License-GPLv2-blue.svg["License: GPL v2", link=https://www.gnu.org/licenses/gpl-2.0] image:https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg["License: LGPL v2.1", link=https://www.gnu.org/licenses/lgpl-2.1] +=== Licensing + +image:https://img.shields.io/badge/License-GPLv2-blue.svg["License: GPL v2", link=https://www.gnu.org/licenses/gpl-2.0] image:https://img.shields.io/badge/License-LGPL%20v2.1-blue.svg["License: LGPL v2.1", link=https://www.gnu.org/licenses/lgpl-2.1] [.text-justify] -Elmer software is licensed under GPL except for the ElmerSolver library which is licensed under LGPL license. Elmer is mainly developed at CSC - IT Center for Science, Finland. However, there have been numerous contributions from other organizations and developers as well, and the project is open for new contributions. More information about Elmer's licensing http://www.elmerfem.org/blog/license/[here]. +Elmer software is licensed under GPL except for the ElmerSolver library which is licensed under LGPL license. Elmer is mainly developed at CSC - IT Center for Science, Finland. However, there have been numerous contributions from other organizations and developers as well, +and the project is open for new contributions. More information about Elmer's licensing link:license_texts/ElmerLicensePolicy.txt[here]. -=== Package managers: +=== Package managers [.text-center] image::https://repology.org/badge/vertical-allrepos/elmerfem.svg["Packaging status", link=https://repology.org/project/elmerfem/versions] -==== Chocolatey: +==== Chocolatey [.text-center] image:https://img.shields.io/chocolatey/dt/elmer-mpi["Chocolatey", link=https://chocolatey.org/packages/elmer-mpi] -=== Social: +=== Social [.text-justify] Here on https://discordapp.com/invite/NeZEBZn[this Discord channel] you may ask for help or dicuss different Elmer related matters: @@ -116,14 +120,13 @@ Ask your questions on Reddit: image:https://img.shields.io/reddit/subreddit-subscribers/ElmerFEM["Subreddit subscribers", link=https://www.reddit.com/r/ElmerFEM/] -=== Computational Glaciology "Elmer/Ice": +=== Computational Glaciology "Elmer/Ice" * http://elmerice.elmerfem.org[Elmer/Ice community web site] * https://github.com/ElmerCSC/elmerfem/tree/elmerice/elmerice/[Elmer/Ice README] -=== Other links: - +=== Other links * http://www.elmerfem.org/[Elmer Blog] * https://www.csc.fi/web/elmer[official CSC homepage] diff --git a/ReleaseNotes/release_9.0.md b/ReleaseNotes/release_9.0.md new file mode 100644 index 0000000000..3b7e3fc624 --- /dev/null +++ b/ReleaseNotes/release_9.0.md @@ -0,0 +1,233 @@ +Elmer Release Notes for version 9.0 +=================================== + +Previous release: **8.4** +Period covered: **Dec 18 2018 - Nov 10 2020** +Number of commits: **~1340** (excluding merges) + +These release notes provide information on the most essential changes. +You can get a complete listing of commit messages, for example, with: +git log --since="2018-12-18" > log.txt + +Apart from the core Elmer team at CSC (Juhani K., Mika M., Juha R., Peter R., Thomas Z.) +git log shows contributions from Daniel B., Denis C., Eef v. D., Eelis T., Fabien G-C, +Foad S. F., Fredrik R., Olivier G., Joe T., Luz P., Mondher C., Rupert G., Sami I., +Sami R., Samuel C., and Saeki T. to this release. + +Additionally there are many ongoing developments in several branches +that have not been merged to this release and are not therefore covered here. +Also sometimes the code has been passed on by the original author by other means than the +git, and in such cases the names may have been accidentally omitted. + +The contributions of all developers are gratefully acknowledged! + + +New Solver Modules +------------------ + +### IncompressibleNSVec +- Incompressible Navier-Stokes solver utilizing vectorized and threaded assembly +- Includes built-in support for block preconditioning (Schur complement approximation included) +- Includes non-Newtonian material laws +- Intended for Elmer/Ice community but also other may find it useful. + + +### BeamSolver3D +- Solver for the Timoshenko equations of elastic beams embedded in 3-D space (see Elmer Models Manual for documentation) + +### GmshReader +- Reads the mesh and results from simple Gmsh file format (that can be written by ElmerSolver as well) +- Solver includes interpolation of the fields to the current mesh +- May be used for hierarchical simulations where results are inherited from previous simulations + +### ModelMixedPoisson +- A general-purpose mixed FEM solver for the Poisson equation (see Elmer Models Manual for documentation) +- Employs a div-conforming (face) finite element approximation + +### SpringAssembly +- A generic utility to add node-wise springs and masses to structural models (see Elmer Models Manual for documentation) + +### MarchingODESolver +- A solver that can compute ordinary differential equations on a moving mesh. +- It is assumed that the mesh is structured and there is a known draw speed. This + makes it possible to relate timestep and mesh parameter directly with each other. + + +Enhanced Solver Modules +----------------------- + +### ElasticSolve +- Adding a new UMAT material model is simplified: compilation with an elmerf90 command is sufficient +- The state variables of UMAT material model can be written to a result file and visualized +- UMAT implementation updated to support axial symmetry + +### EMWaveSolver +- The solver updated to support the basis functions of second order and simulation in 2D +- The solver is now documented in Elmer Models Manual + +### MagnetoDynamics +- Fixes and generalization to the source projection (the determination of Jfix). +- A surface impedance condition for the time-harmonic AV model +- Thin region formulation for 1D wires in transient analysis +- Magnetic anisotropy (a complex-valued reluctivity tensor) enabled for the time-harmonic AV model + +### MagnetoDynamics2D +- A velocity field can be given to add a Lorentz term to the equations +- Coreloss a posteriori formulas (Bertotti + extended Bertotti) + +### MagnetoDynamicsCalcFields +- Enabled postprocessing in the case of a complex-valued reluctivity tensor +- Enabled the computation of magnetic co-energy + +### ResultOutputSolver +- Vtu format: + - Enable saving of pieces, i.e. bodies and boundaries + - Improved saving of elemental, DG and IP fields +- Gmsh format: + - Improved use of masking features in output + +### ShellSolver +- Eigenanalysis with the shell solver enabled +- Spring, resultant force and couple BCs added +- Combined analysis of 2-D shells and 1-D beams enabled +- Fully coupled analysis of 2-D shells and 3-D solids enabled (still subject to some geometric constraints on the mesh) +- Partial support for using an alternate formulation with drilling degrees of freedom + +### StructuredMeshMapper +- Enable arbitrary number of layers, before limited to three. + +### HeatSolver +- A new tentative vectorized version: HeatSolverVec +- Enable symmetric 3D cases for view factor computation to obtain significant timesavings +- Make Gebhart factors linear system symmetric, if possible "ViewFactor Symmetry" + +### StressSolver +- Added a Maxwell visco-elastic model to linear elasticity solver +- Possible also to be run as incompressible (introducing pressure variable) +- Optional pre-stress advection term for layered Earth-deformation model + +### WaveSolver +- The solver can be used to model harmonic and eigenmode cases as well. + +### ParticleAdvector +- Allow particles to be sent from Gaussian integration points as well. This is beneficial +for robustness since they are not located at surface. +- Local integration time based on local Courant number. + + + +ElmerSolver library functionality +--------------------------------- + +### Treatment of block systems +- The block matrix approach for solving complicated problems has been enhanced. + Currently the block approach can be used in several ways during some stage of the solution. + 1. Split up monolithic equations into subproblems that are easier to solve (e.g. IncompressibleNS) + 2. Combine linear multiphysical (coupled) problems into a block matrix (e.g. FSI problems) +- For problems belonging to class 1) we may perform recreation of a monolithic matrix. This will + allow better use of standard linear algebra to utilize direct solvers, or change the system to + be harmonic or eigenvalue problem. +- For the documentation of utilizing block-matrix construct in connection with + the fully coupled simulation of multiphysical problems see the new chapter + "Block-matrix construct to build tightly coupled solvers" in ElmerSolver Manual. + +### More economical integration rules +- A collection of economical Gauss quadrature rules for prismatic elements are introduced to replace + tensor product rules for quadrilateral p-elements when 1 < p <= 8. The tensor + product rule with n = (p+1)**2 points is now replaced by more economical ones. + +### Dirichlet BCs for div-conforming vector finite elements (face elements) +- A sif command of the form Q {f} j = Real ... can be used to specify vector-valued data whose + normal component is then used to integrate the values of DOFs for vector-valued interpolation of the data. + Here Q is an Elmer variable which is approximated with face finite elements. + +### Conforming BCs by elimination +- System can identify conforming boundaries such that dofs related to nodes or edges on opposing sides may be + assembled into one degree of freedom. +- This decreases the size of the linear system and is numerically favourable. +- Antiperiodicity may be included. For vector-valued problems all components must be treated alike. +- Conforming BCs for edge dofs may consider the direction of edge. +- See test cases with "Apply Conforming BCs" and "Conforming BC" defined. + +### Improved internal partitioning with Zoltan +- Enable internal partitioning with Zoltan to honor connected boundaries. + +### Enable primary solver to call other solvers +- For documentation see the section "Solver execution by a master solver" in ElmerSolver Manual. +- Enables calling before and after solving the primary problem. +- Also possible to call before and after each nonlinear iteration. + + +### Anderson Acceleration for nonlinear systems +- Implemented a version of Anderson Acceleration where previous solutions and + residuals are used to accelerate the nonlinear convergence. +- May increase nonlinear convergence to quadratic, quadratic convergence (Newton's method) is not improved. + +### Swapping meshes on-the-fly +- Implemented library functionality to swap meshes during the simulation. +- Currently no history data is interpolated. + +### ListGetElemental routines +- More flexible routines for obtaining material parameters for the Gaussian integration points. +- Detects automatically what kind of fields the dependency depends on (nodal, DG, elemental, IP points) +- Vectorized versions to be used with vectorized finite element assembly + +### View factors +- Allow computation of view factors in 3D cases with symmetry. +- Speed-up computation for cases where emissivity not equal to one. +- Enable view factors to be used in conjunction with DG (in HeatSolveVec) + + +### Run Control +- Enable external loop control over the simulation. +- May be used in optimization and parametric scanning etc. +- Applicable also to transient systems as the variable "time" is not used for the control level. + + +### Inline parameters +- Enable inline keywords -rpar and -ipar +- They are followed by the number of argument + values of the arguments. + +### Generic source control +- We may tune a r.h.s. load vector such that the solution (or reaction force) at + given node is the desired one. +- Mimics the old Smart Control operation of HeatSolve but on a library level. + +ElmerGrid +--------- +- Fixes for UNV, mptxt and Gmsh file format import. +- Tentative reader for FVCOM format +- Add possibility to define seed for Metis partitioning (-metisseed). +- Maintain entity names in extrusion +- ElmerGrid and its plugin under ElmerGUI were harmonized such that they use the same codebase. + + +ElmerGUI +-------- +Huge number of improvements by Saeki! Highlights include: +- Object browser to view the case at a glance and to easily access the most windows. +- Removed sif auto-generation functionality to avoid unintended overwriting of sif file. +- "Generate, save and run" button to quickly run the case modifed via GUI. +- "Save and run" button on sif window to quickly run the case modifed via sif window. +- Postprocessor button selectable from ElmerVTK, ElmerPost or ParaView. +- "New project..." menu as an alternative way to start a new project. +- Seperated "Save project as..." menu from "Save project" menu to save the project in a different directory. +- Improved and more robust project loading +- "Preference" menu on sif window and on solver log window for syntax highlighting and font selection. +- "Recent projects" in File menu for quick loading of recently used projects. +- Improved ElmerVTK postprocessor (reading simple .vtu file, bottom toolbar including time-step control and displace button, etc) + +Configuration & Compilation +--------------------------- +- New Windows installer utilizing msys2 + - either with or without ElmerGUI + with or without MPI installers + +Elmer/Ice +--------- +- New features in Elmer/Ice are documented in elmerfem/elmerice/ReleaseNotes/release_elmerice_9.0.md + +Other +----- +- FreeCADBatchFEMTools improvements and added tests + + diff --git a/docker/elmerice.dockerfile b/docker/elmerice.dockerfile index 2234654220..b7c85e066e 100644 --- a/docker/elmerice.dockerfile +++ b/docker/elmerice.dockerfile @@ -8,6 +8,8 @@ WORKDIR /home RUN printf "Acquire::http::Pipeline-Depth 0;\nAcquire::http::No-Cache true;\nAcquire::BrokenProxy true;" \ >> /etc/apt/apt.conf.d/99fixbadproxy +ENV DEBIAN_FRONTEND="noninteractive" + # Add the necessary packages to compile Elmer/Ice RUN apt update -o Acquire::CompressionTypes::Order::=gz && apt upgrade -y && apt install -y \ build-essential \ diff --git a/elmergrid/src/egconvert.c b/elmergrid/src/egconvert.c index 553f317f7b..c40d26f4c8 100755 --- a/elmergrid/src/egconvert.c +++ b/elmergrid/src/egconvert.c @@ -1408,7 +1408,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr if(info) printf("Allocating for %d knots and %d %d-node elements.\n", noknots,noelements,maxnodes); AllocateKnots(data); - if(info) printf("reading the nodes\n"); + if(info) printf("Reading the nodes\n"); for(i=1;i<=noknots;i++) { GETLINE; if (dim == 2) @@ -1465,7 +1465,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr data->topology = topology; } - if(0) printf("reading %d element topologies with %d nodes for %s\n", + if(info) printf("reading %d element topologies with %d nodes for %s\n", elems,nodes,entityname); for(entity=1;entity<=maxentity;entity++) { @@ -1498,7 +1498,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr ReorderFidapNodes(data,i,nodes,typeflag); if(data->elementtypes[i] == 0) { - printf("******** nolla\n"); + printf("Elementtype is zero!\n"); } if(entity) data->material[i] = entity; @@ -1510,7 +1510,8 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr break; case 10: - if(info) printf("reading the velocity field\n"); + dim = 3; + if(info) printf("Reading the velocity field\n"); CreateVariable(data,2,dim,0.0,"Velocity",FALSE); vel = data->dofs[2]; for(j=1;j<=noknots;j++) { @@ -1524,8 +1525,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr break; case 11: - - if(info) printf("reading the temperature field\n"); + if(info) printf("Reading the temperature field\n"); CreateVariable(data,1,1,0.0,"Temperature",FALSE); temp = data->dofs[1]; for(j=1;j<=noknots;j++) { @@ -1549,7 +1549,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr if(data->topology[i][j] > maxknot) maxknot = data->topology[i][j]; if(maxknot > noknots) { - if(info) printf("renumbering the nodes from 1 to %d\n",noknots); + if(info) printf("Renumbering the nodes from 1 to %d\n",noknots); ind = ivector(1,maxknot); for(i=1;i<=maxknot;i++) @@ -1577,7 +1577,7 @@ int LoadFidapInput(struct FemType *data,struct BoundaryType *boundaries,char *pr if(info) printf("Finished reading the Fidap neutral file\n"); ElementsToBoundaryConditions(data,boundaries,FALSE,TRUE); - RenumberBoundaryTypes(data,boundaries,TRUE,0,info); + /* RenumberBoundaryTypes(data,boundaries,TRUE,0,info); */ return(0); } @@ -1984,7 +1984,7 @@ int LoadAnsysInput(struct FemType *data,struct BoundaryType *bound, FindPointParents(data,bound,boundarynodes,nodeindx,boundindx,info); if(namesexist) { - int bcind,*bctypes=NULL,*bctypeused=NULL,*bcused=NULL,newsides; + int bcind=0,*bctypes=NULL,*bctypeused=NULL,*bcused=NULL,newsides=0; data->bodynamesexist = TRUE; if(bound[0].nosides) { @@ -3541,13 +3541,13 @@ static int LoadGmshInput1(struct FemType *data,struct BoundaryType *bound, static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, - char *filename,int info) + char *filename,int usetaggeom, int info) { int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; int elemind[MAXNODESD2],elementtype; int i,j,k,allocated,*revindx=NULL,maxindx; int elemno, gmshtype, tagphys=0, taggeom=0, tagpart, elemnodes,maxelemtype; - int usetaggeom,tagmat,verno; + int tagmat,verno; int physvolexist, physsurfexist; FILE *in; const char manifoldname[4][10] = {"point", "line", "surface", "volume"}; @@ -3564,10 +3564,10 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, maxnodes = 0; maxindx = 0; maxelemtype = 0; - usetaggeom = FALSE; physvolexist = FALSE; physsurfexist = FALSE; - + usetaggeom = FALSE; + omstart: for(;;) { @@ -3748,11 +3748,6 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, ElementsToBoundaryConditions(data,bound,FALSE,info); - /* The geometric entities are rather randomly numbered */ - if( usetaggeom ) { - RenumberBoundaryTypes(data,bound,TRUE,0,info); - RenumberMaterialTypes(data,bound,info); - } data->bodynamesexist = physvolexist; data->boundarynamesexist = physsurfexist; @@ -3763,12 +3758,12 @@ static int LoadGmshInput2(struct FemType *data,struct BoundaryType *bound, static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, - char *filename,int info) + char *filename,int usetaggeom, int info) { int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; int elemind[MAXNODESD2],elementtype; int i,j,k,l,allocated,*revindx=NULL,maxindx; - int elemno, gmshtype, tagphys=0, taggeom=0, tagpart, elemnodes,maxelemtype; + int elemno, gmshtype, tagphys=0, tagpart, elemnodes,maxelemtype; int tagmat,verno; int physvolexist, physsurfexist,**tagmap,tagsize,maxtag[4]; FILE *in; @@ -3788,6 +3783,7 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, maxelemtype = 0; physvolexist = FALSE; physsurfexist = FALSE; + usetaggeom = TRUE; /* The default */ for(i=0;i<4;i++) maxtag[i] = 0; @@ -3864,13 +3860,14 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, int nobound, idum; Real rdum; + usetaggeom = FALSE; + GETLINE; cp = line; numPoints = next_int(&cp); numCurves = next_int(&cp); numSurfaces = next_int(&cp); numVolumes = next_int(&cp); - if(allocated) { tagsize = 0; @@ -3885,7 +3882,6 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, } for(tagdim=0;tagdim<=3;tagdim++) { - if( tagdim == 0 ) numEnt = numPoints; @@ -3901,8 +3897,9 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, else if( maxtag[tagdim] > 0 ) printf("Maximum original tag for %d %dDIM entities is %d\n",numEnt,tagdim,maxtag[tagdim]); - if(numEnt > 0 && !allocated) printf("Reading %d entities in %dD\n",numEnt,tagdim); - + if(numEnt > 0 && !allocated) { + printf("Reading %d entities in %dD\n",numEnt,tagdim); + } for(i=1; i <= numEnt; i++) { GETLONGLINE; @@ -4188,12 +4185,12 @@ static int LoadGmshInput4(struct FemType *data,struct BoundaryType *bound, static int LoadGmshInput41(struct FemType *data,struct BoundaryType *bound, - char *filename,int info) + char *filename,int usetaggeom, int info) { int noknots = 0,noelements = 0,nophysical = 0,maxnodes,dim,notags; int elemind[MAXNODESD2],elementtype; int i,j,k,l,allocated,*revindx=NULL,maxindx; - int elemno, gmshtype, tagphys=0, taggeom=0, tagpart, elemnodes,maxelemtype; + int elemno, gmshtype, tagphys=0, tagpart, elemnodes,maxelemtype; int tagmat,verno; int physvolexist, physsurfexist,**tagmap,tagsize,maxtag[4]; FILE *in; @@ -4213,6 +4210,7 @@ static int LoadGmshInput41(struct FemType *data,struct BoundaryType *bound, maxelemtype = 0; physvolexist = FALSE; physsurfexist = FALSE; + usetaggeom = TRUE; /* The default */ for(i=0;i<4;i++) maxtag[i] = 0; omstart: @@ -4303,6 +4301,8 @@ static int LoadGmshInput41(struct FemType *data,struct BoundaryType *bound, int tag,tagdim,nophys,phystag; int nobound, idum; Real rdum; + + usetaggeom = FALSE; GETLINE; cp = line; @@ -4631,12 +4631,12 @@ static int LoadGmshInput41(struct FemType *data,struct BoundaryType *bound, } int LoadGmshInput(struct FemType *data,struct BoundaryType *bound, - char *prefix,int info) + char *prefix,int info) { FILE *in; char line[MAXLINESIZE],filename[MAXFILESIZE]; - int errnum; - + int errnum,usetaggeom; + sprintf(filename,"%s",prefix); if ((in = fopen(filename,"r")) == NULL) { sprintf(filename,"%s.msh",prefix); @@ -4668,14 +4668,14 @@ int LoadGmshInput(struct FemType *data,struct BoundaryType *bound, if( verno == 4 ) { if( minorno == 0 ) - errnum = LoadGmshInput4(data,bound,filename,info); + errnum = LoadGmshInput4(data,bound,filename,usetaggeom,info); else if( minorno == 1 ) - errnum = LoadGmshInput41(data,bound,filename,info); + errnum = LoadGmshInput41(data,bound,filename,usetaggeom,info); else printf("Minor version not yet supported, cannot continue!\n"); } else { - errnum = LoadGmshInput2(data,bound,filename,info); + errnum = LoadGmshInput2(data,bound,filename,usetaggeom,info); } } else { fclose(in); @@ -4688,6 +4688,13 @@ int LoadGmshInput(struct FemType *data,struct BoundaryType *bound, errnum = LoadGmshInput1(data,bound,filename,info); } + if( info ) { + if( usetaggeom ) + printf("Using geometric numbering of entities\n"); + else + printf("Using physical numbering of entities\n"); + } + return(errnum); } @@ -4700,7 +4707,7 @@ int LoadFvcomMesh(struct FemType *data,struct BoundaryType *bound, int elemind[MAXNODESD2],elementtype; int i,j,k,allocated,*revindx=NULL,maxindx; int elemnodes,maxelemtype,elemtype0,bclines; - int usetaggeom,tagmat,bccount; + int tagmat,bccount; int *bcinds,*bctags,nbc,nbc0,bc_id; FILE *in; char *cp,line[MAXLINESIZE]; @@ -4717,7 +4724,6 @@ int LoadFvcomMesh(struct FemType *data,struct BoundaryType *bound, maxnodes = 0; maxindx = 0; maxelemtype = 303; - usetaggeom = FALSE; noelements = 0; bclines = 0; @@ -4838,7 +4844,7 @@ int LoadGeoInput(struct FemType *data,struct BoundaryType *bound, int elemind[MAXNODESD2],elementtype; int i,j,k,allocated,*revindx=NULL,maxindx; int elemnodes,maxelemtype,elemtype0; - int usetaggeom,tagmat; + int tagmat; FILE *in; char *cp,line[MAXLINESIZE]; @@ -4854,7 +4860,6 @@ int LoadGeoInput(struct FemType *data,struct BoundaryType *bound, maxnodes = 0; maxindx = 0; maxelemtype = 0; - usetaggeom = FALSE; omstart: diff --git a/elmergrid/src/egextra.c b/elmergrid/src/egextra.c index a62e2a3388..8185895fb1 100755 --- a/elmergrid/src/egextra.c +++ b/elmergrid/src/egextra.c @@ -113,15 +113,9 @@ int SaveBoundary(struct FemType *data,struct BoundaryType *bound, GetElementSide(bound->parent[i],bound->side[i],bound->normal[i],data,sideind,&sideelemtype); - fprintf(out,"%-12.4le %-12.4le %-12.4le %-12.4le ", + fprintf(out,"%-12.4le %-12.4le %-12.4le %-12.4le\n", data->x[sideind[0]],data->x[sideind[1]], data->y[sideind[0]],data->y[sideind[1]]); - for(k=0;kevars[k]) { - if(bound->points[k] == 1) - fprintf(out,"%-10.4le ",bound->vars[k][i]); - } - fprintf(out,"\n"); } fclose(out); @@ -256,21 +250,6 @@ int SaveBoundariesChain(struct FemType *data,struct BoundaryType *bound, ind = bound[j].chain[i]; fprintf(out,"%-10.4le %-10.4le %-6d ", data->x[ind],data->y[ind],ind); - for(k=0;kedofs[k] == 1) fprintf(out,"%-10.4le ",data->dofs[k][ind]); @@ -300,9 +279,6 @@ int SaveBoundariesChain(struct FemType *data,struct BoundaryType *bound, } fprintf(out,"col3: node indices\n"); col = 3; - for(k=0;kedofs[k] == 1) fprintf(out,"col%d: %s\n",++col,data->dofname[k]); diff --git a/elmergrid/src/egmesh.c b/elmergrid/src/egmesh.c index d61e888494..cbc5aba268 100755 --- a/elmergrid/src/egmesh.c +++ b/elmergrid/src/egmesh.c @@ -1580,6 +1580,10 @@ void DestroyKnots(struct FemType *data) free_Rvector(data->y,1,data->noknots); free_Rvector(data->z,1,data->noknots); + data->noknots = 0; + data->noelements = 0; + data->maxnodes = 0; + if(data->nocorners > 0) free_Ivector(data->corners,1,2*data->nocorners); } @@ -1650,12 +1654,6 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, /* The free boundary conditions are not allowed if the negative keywords are used. */ -#if 0 - /* This has been eliminated since it just causes confusion */ - if(sidemat == MAT_ORIGO && material1 != MAT_ORIGO && - (material1 <= MAT_BIGGER || material2 <= MAT_BIGGER)) continue; -#endif - /* Either material must be the one defined. */ if( material1 >= 0 && material1 != sidemat) continue; if( material2 >= 0 && material2 != thismat) continue; @@ -1736,9 +1734,6 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, bound->created = TRUE; bound->nosides = size = nosides; bound->coordsystem = data->coordsystem; - bound->fixedpoints = 1; - bound->open = FALSE; - bound->maparea = 0; bound->types = Ivector(1,nosides); bound->side = Ivector(1,nosides); bound->side2 = Ivector(1,nosides); @@ -1750,9 +1745,6 @@ int CreateBoundary(struct CellType *cell,struct FemType *data, bound->echain = FALSE; bound->ediscont = FALSE; - for(i=0;ievars[i] = FALSE; - goto startpoint; } @@ -1774,9 +1766,6 @@ int AllocateBoundary(struct BoundaryType *bound,int size) bound->created = TRUE; bound->nosides = size; - bound->fixedpoints = 1; - bound->open = FALSE; - bound->maparea = 0; bound->echain = FALSE; bound->ediscont = FALSE; @@ -1798,9 +1787,6 @@ int AllocateBoundary(struct BoundaryType *bound,int size) bound->normal[i] = 1; } - for(i=0;ievars[i] = FALSE; - return(0); } @@ -1825,11 +1811,8 @@ int DestroyBoundary(struct BoundaryType *bound) free_Ivector(bound->side2,1,nosides); free_Ivector(bound->parent,1,nosides); free_Ivector(bound->parent2,1,nosides); - - for(i=0;ievars[i]) { - bound->evars[i] = 0; - } + free_Ivector(bound->types,1,nosides); + free_Ivector(bound->normal,1,nosides); bound->nosides = 0; bound->created = FALSE; diff --git a/elmergrid/src/egnative.c b/elmergrid/src/egnative.c index ddf82e4cbf..de340b0ae4 100644 --- a/elmergrid/src/egnative.c +++ b/elmergrid/src/egnative.c @@ -133,7 +133,6 @@ void Instructions() printf("15) .ep.i : Partitioned ElmerPost format\n"); printf("16) .2dm : 2D triangular FVCOM format\n"); #if 0 - printf("16) .d : Easymesh input format\n"); printf("17) .msh : Nastran format\n"); printf("18) .msh : CGsim format\n"); printf("19) .geo : Geo format\n"); @@ -150,7 +149,6 @@ void Instructions() #if 0 printf("5) .inp : Abaqus input format\n"); printf("7) .fidap : Fidap format\n"); - if(0) printf("8) .n .e .s : Easymesh output format\n"); printf("18) .ep : Fastcap input format.\n"); #endif @@ -214,7 +212,6 @@ void Instructions() printf("-partcell int[3] : the mesh will be partitioned in cells of fixed sizes\n"); printf("-partcyl int[3] : the mesh will be partitioned in cylindrical main directions\n"); #if USE_METIS - if(0) printf("-metis int : mesh will be partitioned with Metis using mesh routines\n"); printf("-metiskway int : mesh will be partitioned with Metis using graph Kway routine\n"); printf("-metisrec int : mesh will be partitioned with Metis using graph Recursive routine\n"); printf("-metiscontig : enforce that the metis partitions are contiguous\n"); diff --git a/elmergrid/src/egtypes.h b/elmergrid/src/egtypes.h index a556bda186..ac3a0c8cb0 100755 --- a/elmergrid/src/egtypes.h +++ b/elmergrid/src/egtypes.h @@ -247,11 +247,8 @@ struct FemType { struct BoundaryType { int created, /* is boundary created? */ nosides, /* sides on the boundary */ - maxsidenodes, /* number of sidenodes on the element */ - fixedpoints, /* number of fixed points allowed */ + maxsidenodes, /* number of sidenodes on the element */ coordsystem, /* coordinate system flag */ - maparea, /* mappings of the area */ - open, /* is the closure partially open? */ echain, /* does the chain exist? */ ediscont, /* does the discontinuous boundary exist */ chainsize; /* size of the chain */ @@ -266,13 +263,7 @@ struct BoundaryType { *normal, /* direction of the normal */ *elementtypes, /* side element types if needed */ **topology, /* topology if needed */ - points[MAXVARS], /* how many points for each side? */ - evars[MAXVARS]; /* does the variables exist? */ - Real totalarea, /* total area of the side */ - areasexist, - *areas, /* side areas */ - *vars[MAXVARS]; /* variables on the sides */ - char varname[MAXVARS][MAXNAMESIZE]; /* variable name */ + points[MAXVARS]; /* how many points for each side? */ }; /* Sometimes one point is discontinuous or there is diff --git a/elmergrid/src/egutils.c b/elmergrid/src/egutils.c index c8235e8b3d..1a61d99a1e 100755 --- a/elmergrid/src/egutils.c +++ b/elmergrid/src/egutils.c @@ -600,7 +600,7 @@ void AddExtension(const char *fname1,char *fname2,const char *newext) } -int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) +int StringToStrings(const char *buf,char args[10][15],int maxcnt,char separator) /* Finds real numbers separated by a certain separator from a string. 'buf' - input string ending to a EOF 'dest' - a vector of real numbers @@ -622,7 +622,7 @@ int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) do { ptr2 = strchr(ptr1,separator); if(ptr2) { - for(i=0;i<10;i++) { + for(i=0;i<15;i++) { args[cnt][i] = ptr1[i]; if(ptr1 + i >= ptr2) break; } @@ -630,7 +630,7 @@ int StringToStrings(const char *buf,char args[10][10],int maxcnt,char separator) ptr1 = ptr2+1; } else { - for(i=0;i<10;i++) { + for(i=0;i<15;i++) { if(ptr1 + i >= buf+totlen) break; args[cnt][i] = ptr1[i]; } diff --git a/elmergrid/src/egutils.h b/elmergrid/src/egutils.h index f81f4ad701..fe4f6656a3 100755 --- a/elmergrid/src/egutils.h +++ b/elmergrid/src/egutils.h @@ -64,7 +64,7 @@ int Minimi(Real *vector,int first,int last); Real Maximum(Real *vector,int first,int last); int Maximi(Real *vector,int first,int last); void AddExtension(const char *fname1,char *fname2,const char *newext); -int StringToStrings(const char *buf,char argv[10][10],int argc,char separator); +int StringToStrings(const char *buf,char argv[10][15],int argc,char separator); int StringToReal(const char *buf,Real *dest,int maxcnt,char separator); int StringToInteger(const char *buf,int *dest,int maxcnt,char separator); int StringToIntegerNoZero(const char *buf,int *dest,int maxcnt,char separator); diff --git a/elmergrid/src/fempre.c b/elmergrid/src/fempre.c index a51403eb9a..f88a562b1e 100755 --- a/elmergrid/src/fempre.c +++ b/elmergrid/src/fempre.c @@ -118,12 +118,6 @@ int main(int argc, char *argv[]) Goodbye(); } } -#if 0 - if(eg.inmethod != 8 && eg.outmethod == 5) { - printf("To write Easymesh format you need to read easymesh format!\n"); - errorstat++; - } -#endif if(eg.timeron) timer_activate(eg.infofile); @@ -216,6 +210,10 @@ int main(int argc, char *argv[]) } if(LoadFidapInput(&(data[nofile]),boundaries[nofile],eg.filesin[nofile],TRUE)) Goodbye(); + + eg.bulkorder = TRUE; + eg.boundorder = TRUE; + if(!eg.usenames) data[nofile].boundarynamesexist = data[nofile].bodynamesexist = FALSE; nomeshes++; @@ -301,9 +299,10 @@ int main(int argc, char *argv[]) boundaries[nofile][i].created = FALSE; boundaries[nofile][i].nosides = 0; } + if (LoadGmshInput(&(data[nofile]),boundaries[nofile],eg.filesin[nofile],TRUE)) Goodbye(); - nomeshes++; + nomeshes++; break; case 15: diff --git a/elmerice/ReleaseNotes/release_elmerice_9.0.md b/elmerice/ReleaseNotes/release_elmerice_9.0.md new file mode 100644 index 0000000000..ddd01b0e63 --- /dev/null +++ b/elmerice/ReleaseNotes/release_elmerice_9.0.md @@ -0,0 +1,50 @@ +Elmer/Ice Release Notes for version 9.0 +======================================= + +Previous release: **8.4** + +Period covered: **18 Dec 2018 - 30 Aug 2020** + +Number of commits: **~110** (excluding merges of other branches) + +These release notes provide information on most essential changes in Elmer/Ice functionalities. Starting from the uppermost directory of the source tree, you can inquire changes inside the elmerice-directory using +```bash +git log --since="2018-12-18" -- elmerice +``` +Overview of changes/enhancements +-------------------------------- +- Documentation of new solvers from now on is placed using MarkDown under `elmerice/Solvers/Documentation/` +- Improvements to inversion methods: + - Introduced new directory structure to distinguish general Adjoint solver routines (`elmerice/Solvers/Adjoint`), solvers solely needed for inversions using SSA (`elmerice/Solvers/AdjointSSA`) and solvers solely needed for invesions using Stokes (`elmerice/Solvers/AdjointStokes`). As there have been a lot of changes/new files introduced, we do not list those below but rather refer the user to the new documentation in MarkDown, which is to be found under (`elmerice/Solvers/Documentation/`). +- Coupling of the GlaDS solvers with the calving solvers in a two-mesh, 3D simulation. Includes a new plume solver that currently relies on the external ODEPack library (not included in the Elmer distribution). +- New thermodynamically consistent model for permafrost with saturated aquifers + + +New Solver/Userfunction Modules +-------------------------------- +- `Calving3D_lset.F90`: Return calving as a level set function (work in progress). +- `CalvingRemeshMMG.F90`: Cut a calving event directly out of a 3D mesh without external gmsh or mesh extrusion. Initial work on allowing calving margins to migrate. +- `PlumeSolver.F90` - associated ODEPack library files: `opkda1.F`, `opkda2.F`, `opkdmain.F`(not included in Elmer reopsitory): Provides plume melt rates across the calving front of a glacier. Fed by output from GlaDS solvers. Simulates a continuous sheet-style plume across entire front, split up into segments defined by frontal nodes and mesh resolution. + +- `CalvingHydroInterp.F90`: Interpolates required variables between 3D ice mesh and 2D hydrology mesh, if using a multi-mesh approach. This is more complicated than it sounds. + +- `HydroRestart.F90`: Allows separate 2D hydrology mesh to be restarted in a multi-mesh simulation. +- `USF_SourceCalcCalving.F90`: User function that calculates the source term for GlaDS as a combination of surface melt (provided in some user-specified variable or input file) and basal melt (worked out automatically from the residual of the TemperateIce solver) +- `BasalMelt3D.F90`: Solver that works out basal melt on ungrounded portions of a glacier. +- `GMValid.F90`: Solver that discriminates between ungrounded areas that are connected to the fjord and isolated ungrounded patches inland. +- `Permafrost.F90`: Collection of solvers for permafrost simlations of a saturated aquifer including heat transfer and phase change as well as solute transport and mechanical deformation (the latter involving the linear elasticity solver) +- `PermafrostMaterials.F90`: Module for material functions given by either a thermodynamically consistent model by Hartikainen or a simplified model by Andersson. Reads default values from `permafrostsolutedb.dat`and `permafrostmaterialdb.dat`. +- `SurfaceBuondaryEnthalpy`: Surface Boundary Condition for steady state thermal regime + +Enhanced Solver/Userfunction Modules +------------------------------------ +- `GlaDSCoupledSolver.F90`: Modified to work on a secondary hydrology mesh (as opposed to the primary ice mesh) and to discriminate properly between fjord-connected ungrounded areas and isolated ungrounded patches inland. Also should work on the basal boundary of an internally extruded 3D mesh. +- `GlaDSchannelSolver.F90`: Changes to achieve the same outcome as above. +- `CalvingRemesh.F90` and `Calving3D.F90`: Changed to avoid interpolating hydrology-specific solvers to the ice mesh after calving. Also changed to allow ice solvers and calving to run at different timestep to hydrology. +- `GroundedSolver.F90`: Minor tweak to allow frontal grounded basal nodes to be listed as grounding-line nodes, so that the plume solver knows where to stick plumes. Introducing the possiblity to use lower surface variable (instead of node coordinate - which is still default) + + +ElmerSolver library functionality +--------------------------------- +- Added Zoltan repartitioning capabilities to permit continuous runtime load balancing and to assist with calving remeshing. +- Added support for MMG3D remeshing/mesh adaptation. diff --git a/elmerice/Solvers/Documentation/CoupledIceHydrologyCalvingPlumesDocumentation.md b/elmerice/Solvers/Documentation/CoupledIceHydrologyCalvingPlumesDocumentation.md new file mode 100644 index 0000000000..7fef9e63b8 --- /dev/null +++ b/elmerice/Solvers/Documentation/CoupledIceHydrologyCalvingPlumesDocumentation.md @@ -0,0 +1,195 @@ +# Hydro-Calving-Plumes Elmer/Ice Documentation +## Modified Files: +* GlaDSCoupledSolver.F90 +* GlaDSchannelSolver.F90 +* MeshUtils.F90 +* CalvingRemesh.F90 +* GroundedSolver.F90 +* InterpVarToVar.F90 +* ModelDescription.F90 +* ElmerSolver.F90 +## New Files: +* PlumeSolver.F90 (and associated ODEPack library files: opkda1.F, opkda2.F, opkdmain.F) +* CalvingHydroInterp.F90 +* HydroRestart.F90 +* USF_SourceCalcCalving.F90 +* BasalMelt3D.F90 +* GMValid.F90 + +## Background +This suite of code changes and additions to the Elmer/Ice framework are designed to allow the coupling and concurrent use of the 3D calving solvers, GlaDS hydrology module and a new plume model within a single Elmer/Ice simulation. They are aimed at situations where you may want to model a large, fast-flowing tidewater glacier and were developed using the test case of Store Glacier in Greenland. If your glacier is small, slow or land-terminating, or you’re modelling an entire ice sheet, you probably don’t need to worry about this, or at least not all of it! In intent, all these changes should be backwards-compatible and shouldn’t break anything that doesn’t use them, but may be using some of the components. If you do find that your old simulations don’t work anymore, it’s probably that I’ve forgotten to put an if statement somewhere to only use the new code in the right circumstances, in which case I apologise. Below I list the changes made to existing files, including new SIF keywords, and the purpose and mode-of-use of new ones, as well as some more general considerations for making everything work. + +## THE MOST IMPORTANT CHANGE +All of this code will only function properly if you put `Calving = Logical True` in the Simulation section of the sif. Without that, none of it will activate properly (or indeed, at all); if you use it, it will all try to run in any of the modified or new files that are used. As things stand, this isn’t therefore terribly modular – you either turn it all on or none of it. If you just want calving, or just GlaDS, they work fine as standalones, but the plume solver requires the use of GlaDS to function (though it can work without the 3D calving solvers being used – this is explained more below). + +## Timestepping +One thing you need to consider with this set-up is the timesteps different parts of the model need to run on. Obviously, liquid water moves and evolves much faster than solid water (i.e. ice), so, in practice, the hydrological solvers want to be running at a much smaller timestep than the ice solvers. However, you can’t run everything at the smallest timestep, unless you’ve got infinite computer time…. So, I found a timestep of 0.1 days for the hydrology worked well, with a timestep of 1 day for the ice (giving a runtime of about 30 hours for one year of simulation). This can be achieved by adding ‘Exec Intervals = 10’ to all the ice solvers (assuming your base timestep is 0.1 days, this means the ice solvers will only run every 10 timesteps, i.e. every day). If you do this, though, you **MUST** also add ‘Timestep Scale = Real 10.0’ to all those solvers, because Elmer is not intelligent enough to recognise, when it comes to time-dependent solvers, that they’re not running at the smallest timestep (essentially, Elmer just looks at the timestep size, which will be the smallest timestep, and assumes everything is running at that, rather than actually checking the time). Otherwise, your ice will evolve 10 times too slowly. Yes, I found this out the hard way. +You should also avoid, if possible, having more than two different timestep sizes for the sake of model stability. So, have your base timestep for the hydrology and some multiple of that for the ice, but try to not start having intermediate ones, because it’s more likely the model will fall over if you do that. + +## Restarting runs +If you need to restart from fully coupled model runs, set up the restart machinery as normal, but then include the HydroRestart solver (see below) as a solver that is executed ‘Before Simulation’. This will allow all the ice and hydrology variables to be restarted properly. If calving has been going on, you’ll also need to make sure you use the right ice mesh – this will be the mesh found in the directory you’ve listed under ‘Remesh Move Mesh Dir’ in the Remesh solver. You’ll find one folder created in that directory for every time the model remeshed, so make sure you pick the right one (usually, this will be the one from the latest timestep, but if you’re restarting from a crashed run, you want to pick the one that lines up with the last result output timestep). + +##Meshes +The main issue with coupling calving and hydrology within Elmer/Ice is that of meshing. The calving solvers rely on modifying the existing ice mesh, creating a new one, and interpolating all the variables between the two. With GlaDS, this doesn’t work, because the channel variables are defined on the edge elements of the mesh itself so i) modifying the mesh causes problems and ii) the channel variables are not obviously interpolatable. To get round this, this setup makes use of two meshes: the standard 3D, internally extruded ice mesh, on which all the usual ice and calving stuff happens, and a second 2D plane hydrology mesh that the GlaDS solvers work on. +As such, you need to create a secondary mesh that the model can use. Ideally, this should be the same footprint as your ice mesh, except you probably want it to extend a bit past the frontal boundary of your ice mesh. The calving model could see the glacier advance or retreat, so the hydrology mesh needs to have a footprint that covers the entire potential area that the calving front could reach. Resolution-wise, things tend to work best if the two meshes are as similar as possible – large differences in resolution will lead to lots of interpolation artefacts (discussed more fully under CalvingHydroInterp.F90 below) that will most likely mess up your simulation eventually. But, you do want the hydrology mesh to be at a finer resolution than the ice, so try a few things and see what works best. +Once you have got your 2D footprint hydrology mesh, you need to get it into Elmer format in the usual way (whatever that might be for you). When you do this, you’ll want to use the -bcoffset option to increment the boundary condition numbers on the new mesh so that they follow on from the boundaries on the ice mesh (so, if you have 4 boundaries on your ice mesh, plus a surface and basal boundary once it’s extruded, you’ll want to offset the hydrology mesh boundaries by 6 so that they start at 7). Once you’ve done that (and before you partition it), you need to edit the mesh.elements file and ensure the **second** column is exclusively populated with the number 2, rather than 1, which is what it’ll be by default. This tells Elmer that this is a different body to the main ice mesh. I find the awk command the easiest way to perform this replacement (awk ‘$2=”2”’). Once you’ve done that, you can partition the mesh as usual. You also need to copy the new mesh directory to create a renamed version that will be used by solvers that need the hydrology mesh, but that you don’t want to create results files from (this is something that may get fixed, but, as it stands, the results output solver will try to output vtu files from every individual solver mesh – if they’re all using the same mesh, all the vtu files will have the same name and will overwrite each other). +When you’ve done all this, there are a couple of things you need to do in the SIF: +* In the Simulation section of the SIF, put `Need Edges 2D = Logical True` and `Need Edges 3D = Logical False` – this will ensure edges are generated on all the 2D hydrology meshes, but not on the 3D ice mesh (edges are important for channel variables) +* In the solver section for GlaDSCoupledSolver, put `Mesh = “.” “"` (assuming your mesh directory is some subdirectory of the working directory the SIF lives in) +* In the solver section for the other two GlaDS solvers (the thickness and channel output ones), and for any solvers where you’re reading in a variable that you want to be applied to the hydrology mesh (say, an expanded basal DEM covering the larger footprint area), put the same line, but replace the path with the equivalent for your renamed secondary hydrology mesh directory +* You need to use the Target Bodies feature to differentiate the ice and hydrology meshes. Assuming Body 1 is your main ice body, put the line `Target Bodies(1) = 1` in the Body 1 section of the SIF +* Then, you need to create a new Body 2 for the hydrology mesh (in theory, this can be any number – you could make it Body 7, if you already have Bodies 2-6, say, I think). This needs the line `Target Bodies(1) = 2` in it, though you could replace the ‘2’ with whatever number you substituted into the mesh.elements file earlier. Though I reckon Elmer would be angry if you, say, used Target Bodies(1) = 3 if there isn’t a Target Bodies(1) = 2 somewhere else. This body will have the same material, but a different equation, body force and initial condition to the ice, just as if you were defining a basal boundary body for the hydrology on a standard 3D mesh +* You can then list all the other bodies you’re going to need to define on the boundaries of the ice mesh in the usual way without needing to put in any more Target Bodies statements +* In the Boundary Condition section, you can list BCs for the hydrology mesh as usual, making sure you use the correct BC number (so, in the example above, you’d probably have BCs 7-10 defined on the hydrology mesh, and 1-6 on the ice mesh). But, make sure that all the hydrology-mesh-defined BCs include `Body ID = 2` (I’m not sure if, assuming you’ve numbered the BCs correctly or pointed them at the correct Target Boundary if you’re not setting them by numbering, you actually need this, but it doesn’t hurt) +* For the initial condition and the equation, just list them as usual – the equation will be any solvers you want to execute on the hydrology mesh, typically the three GlaDS solvers, any reader solvers that are reading variables onto the hydrology mesh, the hydrology weights calculator, and the hydrology restart solver (if relevant – all of these are properly explained below) +* In the relevant Body Force section, a few conditional boundary conditions need to be added (turns out Elmer is perfectly happy with boundary conditions in the body force section – they get applied to all nodes on the relevant body, which can be really useful). These work with some of the changes in GlaDSCoupledSolver.F90 (see below) to stop GlaDS trying to do hydrology on ungrounded parts of the mesh – strictly speaking, I should probably either put it all in the code, or put it all in as body force boundary conditions, but this is the currently working set-up: + * `Hydraulic Potential = Real 0.0` + * `Hydraulic Potential Condition = Variable GMCheck, NormalStress` + * `Real MATC “if(tx(0)>0.0){1}else{(tx(1)*(-1))+1E-32}”` + * The same form of boundary condition should be set for effective pressure, sheet thickness, sheet discharge and sheet storage, as well as the no channel BC (`= Logical True`) + * Water pressure should instead be set to: + * `Water Pressure = Variable Zb` (or whatever you’ve called your basal DEM variable) + * `Real MATC “abs(tx*RhoWS*g)”` + * Because water pressure just depends on depth if the ice is ungrounded + * A simpler form of the first BC above is: + * `Hydraulic Potential = Real 0.0` + * `Hydraulic Potential Condition = Variable GroundedMask` + * `Real MATC “(tx*-1)-0.5”` + * This is fine, provided you don’t have any discrete ungrounded areas inland of the calving front and unconnected to it; if you do, you need the more complicated BC to stop these ungrounded patches turning into unphysical sinks of water + +## Modified Files: +### GlaDSCoupledSolver.F90 +Modifications here are about making sure everything points at Solver % Mesh, rather than Model % Mesh, and to do with correctly importing and saving the other hydrology variables defined on the other hydrology solvers. There are also several blocks that deal with determining whether to ignore elements based on their groundedness (if it’s ungrounded, there’s no hydrology, though the model will treat isolated ungrounded patches inland as grounded here, because otherwise they become unphysical sinks of water – this discrimination is done based on the GMCheck variable from GMValid.F90), and a few lines that stop channel area from blowing up to stupid proportions, which is a problem on fine-resolution meshes. These are activated by specifying `Max Channel Area = Real…` and `Max Sheet Thickness = Real…` in the solver section of the SIF, should you wish to use them. +In practical terms in the SIF, this doesn’t require anything else new, beyond the listing of the hydrology mesh in the Solver section. Otherwise, all that’s required is a line saying `Need Edges = True` in the solver section, which is picked up by MeshUtils.F90 (see below) and allows the channel variables to be added to the mesh from their (identical) solver mesh. + +### GlaDSchannelSolver.F90 +Similarly, changes here are to point everything at Solver % Mesh and towards the appropriate location of the channel variables on the primary hydrology mesh, rather than the secondary hydrology mesh this solver will initially be using. None of this requires anything new in the SIF. + +### MeshUtils.F90 +All the changes in here are pretty much to do with making sure internally-extruded meshes keep edges after extrusion, to make GlaDS work on them, and then making solver-specific meshes able to have edges without the primary solver variable being defined on them. This should all just work – in the current situation, the only line required is the Need Edges line in the GlaDSCoupledSolver section to make that solver meshes have edges, so it can have the channel variables added to it, even though hydraulic potential isn’t an edge variable. If you’re just using GlaDS on the base of a 3D internally extruded mesh, the relevant keyword is instead `Preserve Edges = Logical True` in the Simulation section of the sif. + +### CalvingRemesh.F90 +Very minor change to stop the solver trying to add the hydrology variables to the post-calving ice mesh. This is activated by a new solver option for the CalvingRemesh solver: +* `Solvers To Ignore(n) = Integer n1 n2 n3…` + * n1, n2, n3 and so on should be the number of the solvers assigned to any of the hydrology meshes; n should be the total number of solvers concerned +The remesh solver needs to be run every timestep for complicated reasons, but will only actually (possibly) remesh if it’s a timestep the rest of the calving solvers have been run on. This is done by an addition to the file that compares the current time to the time a calving event occurred (which is recorded by an addition to Calving3D.F90). If the time is the same (i.e. the other calving solvers were also run this timestep), remeshing proceeds as normal; if the time is different (i.e. we’re on an intermediary hydrology timestep between ice timesteps), then the CalvingOccurs Boolean is set to false, and no remeshing occurs. The extra computational time required by running the solver in this way every timestep is negligible. + +### GroundedSolver.F90 +Now should list all grounded nodes on the frontal ice boundary as grounding-line nodes, which is needed for the PlumeSolver, provided a line in the solver section saying `Front Variable = String “”` is added, specifying the name of a variable defined on the calving front (the toe calving variable is useful here – see below). Also added an option to force the entire domain to be grounded, which can be activated by putting a line saying `All Grounded = Logical True` in the solver section. This is useful because if you’re running a hydrology-only simulation where you want to use two meshes (say, for mesh resolution reasons, or because you want to restart a full calving-hydro-plume simulation from it), you’ll need to use the `Calving = True` switch, which will make GlaDSCoupledSolver look for the groundedness of elements to determine whether it needs to bother with them, so you’ll need to include the GroundedSolver in the sif, but you don’t actually want it to do anything beyond marking everything as grounded. + +### InterpVarToVar.F90 +A couple of modifications to allow it to deal properly with interpolating between 2D and 3D meshes, rather than just between 3D meshes, and for situations where mesh connectivity is not perfect (i.e. it now allocates perms that will actually work). + +### ModelDescription.F90 +One modification to stop it trying to re-allocate a variable in the case of multiple restarts happening in the same simulation (i.e. if you’ve got more than one mesh). Restarting in the usual manner will load the first mesh, which, unless you’ve set things up strangely, should be the ice mesh (which will also be the mesh defined as `Model % Mesh`). The hydrology mesh can be restarted using the HydroRestart solver described below. + +### ElmerSolver.F90 +A couple of minor modifications to stop the model trying to restart all the meshes. If you do nothing, the model will only restart the first mesh, which will usually be the ice mesh, if the `Calving=Logical True` switch is set in the sif. You can also specify in the Simulation section a new keyword, `Meshes To Restart(n) = n1, n2, n3…` if you want to modify this default behaviour. + +## New Files: +### PlumeSolver.F90 +This is the new solver I’ve written that allows meltwater plumes and their resulting melt rate to be modelled at the calving front. The actual plume model itself is a 1D ODE model, which is an adaptation of Tom Cowton’s MITgcm plume model, which is itself an adaptation of Donald Slater’s MATLAB plume model. It uses the ODEPack library (consisting, here, of odpka1.F, odpka2.F and odpkmain.F), which should be compiled alongside it. ODEPack is written in FORTRAN 77 – I made the minimum changes necessary to get it to compile with the elmerf90 compiler, but if the compiler gets updated at some point, it may find more things inside ODEPack that it doesn’t like. Also note that ODEPack is very particular about the format of its inputs and code that calls it (this is all detailed in odpkmain.F), hence why you’ll notice that outdated things like implicit variables and common blocks appear in the bowels of the PlumeSolver.F90 file. ODEPack is not currently included as part of the Elmer repository, and if you download your own version, it won’t compile. We’re working on setting it up to be included (or to use an alternative library), but, for now, you’re best contacting me (samuel.cook@univ-grenoble-alpes.fr) to get the library files. +PlumeSolver.F90 has three main subroutines: +* Plume: this is the wrapper routine that handles the interaction with the rest of Elmer, chiefly getting the necessary inputs from GlaDS and solver options, and then turning the outputs into a melt rate across the calving front +* PlumeSolver: this is the actual plume model that takes the input discharges and works out a resulting plume profile +* SheetPlume: this defines the system of equations actually solved by ODEPack +The Plume subroutine is a substantially modified and expanded version of a solver written by Joe Todd that takes a provided set of plume profiles and melt rates at fixed locations and applies them to the relevant bits of the calving front. All this functionality still exists and works, if anyone wants to use it (and some of it is hijacked by the new code anyway), but I’m not going to detail it here, because it’s not what I’ve focused on. +The overall strategy of the solver is to dynamically model a continuous line plume along the whole calving front. Each grounding-line node on the hydrology mesh is assigned to the nearest basal frontal node on the ice mesh, with the discharge of the plume at that point being the sum of the sheet and channel discharge across all of the hydrology nodes assigned to that ice node. The plumes are all then modelled to get a set of melt-rate profiles across the calving front, and the melt rate at each node on the calving front is then interpolated from this. This allows melt rates across the calving front to vary as the subglacial drainage evolves over time, and avoids the user having to specify the location or size of any of the plumes. +As things stand, the solver assumes the calving front is a flat, vertical plane. The plume model itself can handle non-vertical profiles, but I haven’t yet got round to working out how to extract this information from Elmer/how far the nature of the internally-extruded meshes in Elmer I’m using even allows non-vertical profiles to exist. +As regards solver options and inputs: +* `Plume Melt Mode = String “…”` + * Options are `constant`, `seasonal` or `off` + * If `constant`, you need to specify `Salinity Temp Depth Input File` (see below) + * If `seasonal`, you need to specify `Summer Salinity Temp Depth Input File` and `Winter Salinity Temp Depth Input File` (see below) + * Additionally, you need to specify `Plume Melt Summer Start = Real…` and `Plume Melt Summer Stop = Real…` in model time to say when your summer conditions start and stop +* `(Summer/Winter) Salinity Temp Depth Input File = File “Name”` + * This is the file that contains the data on the ambient water conditions. This should be a .csv or other text file with three columns – depth (ordered from 0 downwards, so a depth of 50 m should be expressed as -50 in the file), salinity (PSU) and temperature (°C) + * Because of the strategy used by the solver to make plumes work properly in parallel, the sequence of depths in the ambient data file is that used to model the plume (it means the solver knows that the size of all the output profiles will be the same, which makes MPI much simpler). Therefore, the ambient data file **must** extend to the maximum depth of the calving front, so that any plume can be emplaced at the correct depth. This may mean you have to just add a few lines on the bottom of the file, copying your deepest data point downwards in increments of a few metres. Essentially, that’s what the toe calving routine (see below) does anyway, so it’s a reasonable assumption to make +* `Force Toe Calving = Logical True/False` + * This is something Joe implemented, the upshot of which is to extend melt rates downwards, if your ambient data doesn’t extend as deep as the calving front. This stops unphysical toes forming at the calving front and messing up the mesh + * If using this, specify `Exported Variable 1 = “Name”` for the toe calving variable + * It’s useful to export the toe calving variable anyway, even if you don’t want to activate the routine, as it (or a similar variable) is needed by GroundedSolver.F90 to help it define the grounding line +* `Mesh Resolution = Real n` + * This is the nominal mesh resolution at the calving front, which the solver needs to work out the width of each plume segment and how far inland it should look for grounding-line nodes. This should work, even if you have a grounding line a long way inland, because the solver applies a couple of tests to work out if it should ignore a given grounding-line node on the hydrology mesh (e.g. it’s possible to have closed loops of ungrounded areas inland that don’t connect to the front and which the solver should ignore), but you may need to fiddle with this number slightly if you find it’s not doing what it should +Known issues: +* None yet + +### CalvingHydroInterp.F90 +This represents the other major block of new code written as part of this suite. It handles the interpolation of necessary variables between the ice and hydrology meshes, but also moves read-in variables (using GridDataReader or similar) from their solver-specific secondary hydrology meshes to the primary hydrology mesh associated with GlaDSCoupledSolver. It also corrects interpolation artefacts that will otherwise nix your simulation sooner or later, and ensures conservation of the temperature residual (one of the interpolated variables) to stop the glacier accidentally destroying or creating some energy…. +The file contains two main subroutines, imaginatively titled “IceToHydroInterp” and “HydroToIceInterp”. Make sure you get them the right way round. IceToHydroInterp interpolates the ice normal stress, velocity, grounded mask and temperature residual over to the hydrology mesh and then spends a lot of time clearing up artefacts and conserving the temperature residual. HydroToIceInterp is much simpler, as the hydrology mesh is usually finer than the ice mesh, so the interpolation routine doesn’t create anywhere near as many artefacts in problematic locations. Therefore, it pretty much just interpolates the water pressure, effective pressure and sheet discharge onto the ice mesh. +There are also two small subroutines: “HydroWeightsSolver” and “IceWeightsSolver”. These calculate the boundary weights used in the main routines (the reason this happens in a separate solver is complicated – suffice to say it does exist). These need to be called as solvers before the relevant interpolation routines (IceToHydro or HydroToIce) are called, otherwise they’ll crash. IceWeightsSolver needs to run every time the ice mesh is updated (probably every n timesteps); HydroWeightsSolver every time the hydrology mesh is updated (probably never, so it can just run once at the start of the simulation). +Solver options and inputs (for IceToHydroInterp; others have nothing fancy): +* `Load Reader Variables = Logical True/False` + * Set this option to true if you’ve got variables read onto secondary hydrology meshes (say, a basal DEM or a runoff raster) that need to be transferred to the main hydrology mesh +* `Number of Variables To Read = Integer n` + * If you are loading read-in variables, say how many there are. Note: the solver is currently only set up to deal with a maximum of 10 variables; if you have more than that, you’ll need to modify the source code +* `Reader Solver 1 = Integer n` and `Reader V1 = String “name”` + * If loading variables, say which solver number the reader solver is and what the name of the variable is + * You should provide as many `Reader Solver` and `Reader V` entries as the number you’ve defined under `Number Of Variables To Read`, numbered sequentially +* `Reference Node(3) = Integer x y z` and `Threshold Distance = Real n` + * These are used as part of the artefact correction for the grounded mask – if specified, all nodes on the hydrology mesh greater than the Threshold Distance from the Reference Node will be automatically set to grounded. This can be useful if dealing with artefacts a long way inland +* `Side = Logical True/False` + * This isn’t a solver option, but something that should be set in the boundary condition section of the SIF. The interpolation routine tends to create a lot of artefacts on the lateral boundaries of the domain – if you set `Side = Logical True` in the boundary condition sections for the sidewalls of the hydrology mesh, this will force them to be grounded and remove the frequent ungrounded artefacts. +Known issues: +* None, though the artefact correction could probably be improved + +### HydroRestart.F90 +Largely a direct copy of the Restart() subroutine within the Elmer source code, with a few modifications to disentangle it from all the other restart machinery and to make it pick up the right variables from the right place and send them to the right place. +Solver options and inputs: +* `hp: Restart Variable 1 = String “Name”` + * All the variables that should be restarted on the primary hydrology mesh (i.e. all those that are normally calculated by or associated with GlaDSCoupledSolver) should be listed like this – just number the entries consecutively +* `channel: Restart Variable 1 = String “Name”` + * Same as above, but for the channel variables (i.e. channel area and channel flux) +* `sheet: Restart Variable 1 = String “Name”` + * Same as above, but for the sheet variables (i.e. sheet thickness – it’s the only one associated with the separate sheet thickness solver) +* The changes to GlaDSCoupledSolver.F90 will then ensure all the variables end up associated with the primary hydrology mesh, but it’s best to restart them on their own solver meshes +Known issues: +* None + +### USF_SourceCalcCalving.F90 +This is a USF that calculates the Hydraulic Potential Volume Source term required in the Body Force section of the SIF for GlaDS. If provided with a surface runoff variable (I usually load it in from a raster) and the temperature residual variable, it will calculate the resulting internal melt and add on the surface melt for each node on the hydrology mesh (or, at the base of your 3D ice mesh, if you’re using GlaDS without all the other bells and whistles), so you can easily vary the source term spatially across your domain. +USF options and inputs: +* These all go in the same Body Force section as where you define the source term +* `Internal Melt = Logical True/False` + * Switch for whether you want to work out internal melt or not +* `Surface Melt = Logical True/False` + * Same for surface melt +* `Internal Melt Variable Name = String “Name”` + * If you are using internal melt, give the name of the variable that you want it worked out from (note: the USF is set up on the assumption that this will be the temperature residual from the TemperateIceSolver. If you want to use something else, you’ll need to change the code in the USF) +* `Surface Melt Variable Name = String “Name”` + * If using surface melt, the name of the variable that contains it +* Finally, when defining the source term, you call this USF just like any other, and it does not matter what variable you use in the call – the USF will ignore it. + +### BasalMelt3D.F90 +This is a very simple solver written by Joe Todd that applies a specified basal melt rate to any ungrounded parts of the glacier base. +Solver options and inputs: +* `Basal Melt Stats File = String …` + * The path to write a file containing some basal melt stats to. +* `GroundedMask Variable = String …` + * The name of the variable used by the grounded solver +* `Basal Melt Mode = String …` + * Can be ‘seasonal’ or ‘off’. The latter is fairly self-explanatory; the former requires the following additional options: +* `Basal Melt Summer Rate = Real …` + * The rate to apply basal melt at in summer. +* `Basal Melt Winter Rate = Real …` + * The rate to apply basal melt at in winter. +* `Basal Melt Summer Start = Real …` + * The time in the simulation to begin using summer melt rates (expressed as a number between 0 and 1) +* `Basal Melt Summer Stop = Real …` + * The time in the simulation to begin using summer melt rates (expressed as a number between 0 and 1) + +### GMValid.F90 +This is a very simple solver that’s more-or-less just a stripped-down copy of BasalMelt3D.F90 and exists to set up a mask variable for which ungrounded areas are connected to the fjord and which aren’t. +Solver options and inputs: +* None – just the usual lines to define the equation, etc. + +## Problems +If you find something that doesn’t work or you can’t easily fix or isn’t listed here, email Samuel Cook (samuel.cook@univ-grenoble-alpes.fr) + +## Known Bugs +* There is an unresolved issue somewhere in the calving code that means, sometimes, the coupled model will just crash or hang at the end of the SwitchMesh routine for a reason I haven’t yet pinned down – it’s something to do with particularly complicated or large calving events (I think it's down to calving producing strange meshes). If this happens, rerunning the simulation will usually fix things (because the calving code has an element of randomness, the same event won’t recur in exactly the same way). However, be prepared to give it a few goes (5-6) if it keeps happening. If you’re running long simulations, the model **WILL** crash on this at some point, so the best plan is to just restart from the last full result output timestep the run completed and carry on from there, rather than trying to get a complete run in one go. If the model just keeps crashing at the same point, consider running the previous simulation you’ve restarted from – I found that helps. + +## Other Modifications +There have also been other changes to the source code that are not of particular relevance to the end user, but are included here for completeness: +* New MeshTag field defined as a new integer field on the Mesh_t variable type in **Types.F90**. This integer is incremented by **CalvingRemesh.F90** every time remeshing occurs and is then used by all solvers and USFs to decide whether memory allocation needs to be redone, rather than relying on the Mesh % Changed Boolean, which is unreliable +* Line adding CalvingTime as an entry to the Model % Simulation list in **Calving3D.F90**, which is then used by **CalvingRemesh.F90** as described above diff --git a/fem/src/BlockSolve.F90 b/fem/src/BlockSolve.F90 index 9871d6cc89..35d23d4ff5 100644 --- a/fem/src/BlockSolve.F90 +++ b/fem/src/BlockSolve.F90 @@ -1787,11 +1787,11 @@ SUBROUTINE StructureCouplingBlocks( Solver ) INTEGER :: i,j,k,ind1,ind2,Novar INTEGER, POINTER :: ConstituentSolvers(:) LOGICAL :: Found - TYPE(ValueList_t), POINTER :: Params + TYPE(ValueList_t), POINTER :: Params, ShellParams TYPE(Matrix_t), POINTER :: A_fs, A_sf, A_s, A_f TYPE(Variable_t), POINTER :: FVar, SVar LOGICAL :: IsPlate, IsShell, IsBeam, IsSolid, GotBlockSolvers - + LOGICAL :: DrillingDOFs Params => Solver % Values ConstituentSolvers => ListGetIntegerArray(Params, 'Block Solvers', GotBlockSolvers) @@ -1843,9 +1843,16 @@ SUBROUTINE StructureCouplingBlocks( Solver ) IF(.NOT. ASSOCIATED( FVar ) ) THEN CALL Fatal('StructureCouplingBlocks','Slave structure variable not present!') END IF + + IF (IsShell) THEN + ShellParams => CurrentModel % Solvers(ind2) % Values + DrillingDOFs = GetLogical(ShellParams, 'Drilling DOFs', Found) + ELSE + DrillingDOFs = .FALSE. + END IF CALL StructureCouplingAssembly( Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & - IsSolid, IsPlate, IsShell, IsBeam ) + IsSolid, IsPlate, IsShell, IsBeam, DrillingDOFs) !IF (IsShell) THEN ! CALL StructureCouplingAssembly_defutils( Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & ! IsSolid, IsPlate, IsShell, IsBeam) diff --git a/fem/src/CMakeLists.txt b/fem/src/CMakeLists.txt index 5c99a09e25..dfbb76cb6b 100644 --- a/fem/src/CMakeLists.txt +++ b/fem/src/CMakeLists.txt @@ -42,7 +42,7 @@ SET(solverlib_SOURCES AddrFunc.F90 NavierStokes.F90 NavierStokesGeneral.F90 cholmod.c InterpolateMeshToMesh.F90 InterpVarToVar.F90 LinearForms.F90 H1Basis.F90 CircuitUtils.F90 BackwardError.F90 ElmerSolver.F90 MagnetoDynamicsUtils.F90 ComponentUtils.F90 - ZirkaHysteresis.F90) + ZirkaHysteresis.F90 SolidMechanicsUtils.F90) SET_PROPERTY(SOURCE MaxwellAxiS.F90 PROPERTY COMPILE_DEFINITIONS FULL_INDUCTION) diff --git a/fem/src/CRSMatrix.F90 b/fem/src/CRSMatrix.F90 index 5a7094d2be..5a0ff6adcb 100644 --- a/fem/src/CRSMatrix.F90 +++ b/fem/src/CRSMatrix.F90 @@ -920,7 +920,7 @@ END SUBROUTINE InsertionSort !------------------------------------------------------------------------------ !> Add a set of values (.i.e. element stiffness matrix) to a CRS format !> matrix. For this matrix the entries are ordered so that first for one -!> dof you got all nodes, and then for second etc. There may be on offset +!> dof you got all nodes, and then for second etc. There may be an offset !> to the entries making the subroutine suitable for coupled monolithic !> matrix assembly. !------------------------------------------------------------------------------ diff --git a/fem/src/ElementDescription.F90 b/fem/src/ElementDescription.F90 index 3e703b0232..9ad6cf0a87 100644 --- a/fem/src/ElementDescription.F90 +++ b/fem/src/ElementDescription.F90 @@ -414,8 +414,8 @@ END SUBROUTINE AddElementDescription !------------------------------------------------------------------------------ -!> Read the element description input file and add the element types to a -!> global list. The file is assumed to be found under the name +!> Read the element description input file and add the element types to a +!> global list. The file is assumed to be found under the name !> $ELMER_HOME/lib/elements.def !> This is the first routine the user of the element utilities should call !> in his/her code. @@ -786,7 +786,11 @@ FUNCTION InterpolateInElement1D( element,x,u ) RESULT(y) s = 0.0d0 DO i=1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) + IF (p(i)==0) THEN + s = s + Coeff(i) + ELSE + s = s + Coeff(i) * u**p(i) + END if END DO y = y + s * x(n) END IF @@ -822,7 +826,11 @@ SUBROUTINE NodalBasisFunctions1D( y,element,u ) s = 0.0d0 DO i=1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) + IF (p(i)==0) THEN + s = s + Coeff(i) + ELSE + s = s + Coeff(i) * u**p(i) + END if END DO y(n) = s END DO @@ -959,19 +967,19 @@ END FUNCTION SecondDerivatives1D !------------------------------------------------------------------------------ -!> Given element structure return value of a quantity x given at element nodes -!> at local coordinate point (u,vb) inside the element. Element basis functions -!> are used to compute the value.This is for 2D elements, and shouldn't probably +!> Given element structure return the value of a quantity x known at element nodes +!> at local coordinate point (u,v) inside the element. Element basis functions +!> are used to compute the value. This is for 2D elements, and shouldn't probably !> be called directly by the user but through the wrapper routine !> InterpolateInElement. !------------------------------------------------------------------------------ FUNCTION InterpolateInElement2D( element,x,u,v ) RESULT(y) !------------------------------------------------------------------------------ TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the partial derivative - REAL(KIND=dp) :: v !< Point at which to evaluate the partial derivative - REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity whose partial derivative we want to know - REAL(KIND=dp) :: y !< value of the quantity y = x(u,v) + REAL(KIND=dp) :: u !< u at the point where the quantity is evaluated + REAL(KIND=dp) :: v !< v at the point where the quantity is evaluated + REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity + REAL(KIND=dp) :: y !< The value of the quantity y = x(u,v) !------------------------------------------------------------------------------ ! Local variables !------------------------------------------------------------------------------ @@ -1057,8 +1065,8 @@ END SUBROUTINE NodalBasisFunctions2D !------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate u of i quantity x given at element nodes at local +!> Given element structure return the value of the first partial derivative with +!> respect to local coordinate u of a quantity x given at element nodes at local !> coordinate point u,v inside the element. Element basis functions are used to !> compute the value. !------------------------------------------------------------------------------ @@ -1365,8 +1373,8 @@ END FUNCTION InterpolateInElement3D SUBROUTINE NodalBasisFunctions3D( y,element,u,v,w ) !------------------------------------------------------------------------------ TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u,v,w !< Point at which to evaluate the partial derivative - REAL(KIND=dp) :: y(:) !< value of the quantity y = x(u,v,w) + REAL(KIND=dp) :: u,v,w !< Point at which to evaluate the basis functions + REAL(KIND=dp) :: y(:) !< The values of the basis functions !------------------------------------------------------------------------------ ! Local variables !------------------------------------------------------------------------------ @@ -1533,47 +1541,47 @@ FUNCTION FirstDerivativeInV3D( element,x,u,v,w ) RESULT(y) l = elt % BasisFunctionDegree BasisFunctions => elt % BasisFunctions -IF ( Elt % ElementCode == 605 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) + IF ( Elt % ElementCode == 605 ) THEN + IF ( w == 1 ) w = 1.0d0-1.0d-12 + s = 1.0d0 / (1-w) - y = 0.0d0 - y = y + x(1) * ( -(1-u) + u*w * s ) / 4 - y = y + x(2) * ( -(1+u) - u*w * s ) / 4 - y = y + x(3) * ( (1+u) + u*w * s ) / 4 - y = y + x(4) * ( (1-u) - u*w * s ) / 4 + y = 0.0d0 + y = y + x(1) * ( -(1-u) + u*w * s ) / 4 + y = y + x(2) * ( -(1+u) - u*w * s ) / 4 + y = y + x(3) * ( (1+u) + u*w * s ) / 4 + y = y + x(4) * ( (1-u) - u*w * s ) / 4 - RETURN -ELSE IF ( Elt % ElementCode == 613 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) + RETURN + ELSE IF ( Elt % ElementCode == 613 ) THEN + IF ( w == 1 ) w = 1.0d0-1.0d-12 + s = 1.0d0 / (1-w) - y = 0.0d0 - y = y + x(1) * ( -( (1-u) * (1-v) - w + u*v*w * s ) + & - (-u-v-1) * ( -(1-u) + u*w * s ) ) / 4 + y = 0.0d0 + y = y + x(1) * ( -( (1-u) * (1-v) - w + u*v*w * s ) + & + (-u-v-1) * ( -(1-u) + u*w * s ) ) / 4 - y = y + x(2) * ( -( (1+u) * (1-v) - w - u*v*w * s ) + & - ( u-v-1) * ( -(1+u) - u*w * s ) ) / 4 + y = y + x(2) * ( -( (1+u) * (1-v) - w - u*v*w * s ) + & + ( u-v-1) * ( -(1+u) - u*w * s ) ) / 4 - y = y + x(3) * ( ( (1+u) * (1+v) - w + u*v*w * s ) + & - ( u+v-1) * ( (1+u) + u*w * s ) ) / 4 + y = y + x(3) * ( ( (1+u) * (1+v) - w + u*v*w * s ) + & + ( u+v-1) * ( (1+u) + u*w * s ) ) / 4 - y = y + x(4) * ( ( (1-u) * (1+v) - w - u*v*w * s ) + & - (-u+v-1) * ( (1-u) - u*w * s ) ) / 4 + y = y + x(4) * ( ( (1-u) * (1+v) - w - u*v*w * s ) + & + (-u+v-1) * ( (1-u) - u*w * s ) ) / 4 - y = y + x(5) * 0.0d0 + y = y + x(5) * 0.0d0 - y = y - x(6) * (1+u-w)*(1-u-w) * s / 2 - y = y + x(7) * ( (1-v-w)*(1+u-w) - (1+v-w)*(1+u-w) ) * s / 2 - y = y + x(8) * (1+u-w)*(1-u-w) * s / 2 - y = y + x(9) * ( (1-v-w)*(1-u-w) - (1+v-w)*(1-u-w) ) * s / 2 + y = y - x(6) * (1+u-w)*(1-u-w) * s / 2 + y = y + x(7) * ( (1-v-w)*(1+u-w) - (1+v-w)*(1+u-w) ) * s / 2 + y = y + x(8) * (1+u-w)*(1-u-w) * s / 2 + y = y + x(9) * ( (1-v-w)*(1-u-w) - (1+v-w)*(1-u-w) ) * s / 2 - y = y - x(10) * w * (1-u-w) * s - y = y - x(11) * w * (1+u-w) * s - y = y + x(12) * w * (1+u-w) * s - y = y + x(13) * w * (1-u-w) * s - RETURN -END IF + y = y - x(10) * w * (1-u-w) * s + y = y - x(11) * w * (1+u-w) * s + y = y + x(12) * w * (1+u-w) * s + y = y + x(13) * w * (1-u-w) * s + RETURN + END IF y = 0.0d0 DO n = 1,elt % NumberOfNodes diff --git a/fem/src/ElementDescription.F90.NEW b/fem/src/ElementDescription.F90.NEW deleted file mode 100644 index c23c76f211..0000000000 --- a/fem/src/ElementDescription.F90.NEW +++ /dev/null @@ -1,12573 +0,0 @@ -!/*****************************************************************************/ -! * -! * Elmer, A Finite Element Software for Multiphysical Problems -! * -! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland -! * -! * This library is free software; you can redistribute it and/or -! * modify it under the terms of the GNU Lesser General Public -! * License as published by the Free Software Foundation; either -! * version 2.1 of the License, or (at your option) any later version. -! * -! * This library is distributed in the hope that it will be useful, -! * but WITHOUT ANY WARRANTY; without even the implied warranty of -! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -! * Lesser General Public License for more details. -! * -! * You should have received a copy of the GNU Lesser General Public -! * License along with this library (in file ../LGPL-2.1); if not, write -! * to the Free Software Foundation, Inc., 51 Franklin Street, -! * Fifth Floor, Boston, MA 02110-1301 USA -! * -! *****************************************************************************/ -! -!/****************************************************************************** -! * -! * Authors: Juha Ruokolainen -! * Email: Juha.Ruokolainen@csc.fi -! * Web: http://www.csc.fi/elmer -! * Address: CSC - IT Center for Science Ltd. -! * Keilaranta 14 -! * 02101 Espoo, Finland -! * -! * Original Date: 01 Oct 1996 -! * -! ******************************************************************************/ - -!-------------------------------------------------------------------------------- -!> Module defining element type and operations. The most basic FEM routines -!> are here, handling the basis functions, global derivatives, etc... -!-------------------------------------------------------------------------------- -!> \ingroup ElmerLib -!> \{ - -#include "../config.h" - -MODULE ElementDescription - USE Integration - USE GeneralUtils - USE LinearAlgebra - USE CoordinateSystems - ! Use module P element basis functions - USE PElementMaps - USE PElementBase - ! Vectorized P element basis functions - USE H1Basis - USE Lists - - IMPLICIT NONE - - INTEGER, PARAMETER,PRIVATE :: MaxDeg = 4, MaxDeg3 = MaxDeg**3, & - MaxDeg2 = MaxDeg**2 - - INTEGER, PARAMETER :: MAX_ELEMENT_NODES = 256 - - ! - ! Module global variables - ! - LOGICAL, PRIVATE :: TypeListInitialized = .FALSE. - TYPE(ElementType_t), PRIVATE, POINTER :: ElementTypeList - ! Local workspace for basis function values and mapping -! REAL(KIND=dp), ALLOCATABLE, PRIVATE :: BasisWrk(:,:), dBasisdxWrk(:,:,:), & -! LtoGMapsWrk(:,:,:), DetJWrk(:), uWrk(:), vWrk(:), wWrk(:) -! !$OMP THREADPRIVATE(BasisWrk, dBasisdxWrk, LtoGMapsWrk, DetJWrk, uWrk, vWrk, wWrk) -! !DIR$ ATTRIBUTES ALIGN:64::BasisWrk, dBasisdxWrk -! !DIR$ ATTRIBUTES ALIGN:64::LtoGMapsWrk -! !DIR$ ATTRIBUTES ALIGN:64::DetJWrk -! !DIR$ ATTRIBUTES ALIGN:64::uWrk, vWrk, wWrk - -CONTAINS - - -!------------------------------------------------------------------------------ -!> Add an element description to global list of element types. -!------------------------------------------------------------------------------ - SUBROUTINE AddElementDescription( element,BasisTerms ) -!------------------------------------------------------------------------------ - INTEGER, DIMENSION(:) :: BasisTerms !< List of terms in the basis function that should be included for this element type. - ! BasisTerms(i) is an integer from 1-27 according to the list below. - TYPE(ElementType_t), TARGET :: element !< Structure holding element type description -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(ElementType_t), POINTER :: temp - - INTEGER, DIMENSION(MaxDeg3) :: s - INTEGER :: i,j,k,l,m,n,upow,vpow,wpow,i1,i2,ii(9),jj - - REAL(KIND=dp) :: u,v,w,r - REAL(KIND=dp), DIMENSION(:,:), ALLOCATABLE :: A, B -!------------------------------------------------------------------------------ - -! PRINT*,'Adding element type: ', element % ElementCode - - n = element % NumberOfNodes - element % NumberOfEdges = 0 - element % NumberOfFaces = 0 - element % BasisFunctionDegree = 0 - NULLIFY( element % BasisFunctions ) - - IF ( element % ElementCode >= 200 ) THEN - - ALLOCATE( A(n,n) ) - -!------------------------------------------------------------------------------ -! 1D bar elements -!------------------------------------------------------------------------------ - IF ( element % DIMENSION == 1 ) THEN - - DO i = 1,n - u = element % NodeU(i) - DO j = 1,n - k = BasisTerms(j) - 1 - upow = k - IF ( u==0 .AND. upow == 0 ) THEN - A(i,j) = 1 - ELSE - A(i,j) = u**upow - END IF - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,upow) - END DO - END DO - -! ALLOCATE( element % BasisFunctions(MaxDeg,MaxDeg) ) - -!------------------------------------------------------------------------------ -! 2D surface elements -!------------------------------------------------------------------------------ - ELSE IF ( element % DIMENSION == 2 ) THEN - - DO i = 1,n - u = element % NodeU(i) - v = element % NodeV(i) - DO j = 1,n - k = BasisTerms(j) - 1 - vpow = k / MaxDeg - upow = MOD(k,MaxDeg) - - IF ( upow == 0 ) THEN - A(i,j) = 1 - ELSE - A(i,j) = u**upow - END IF - - IF ( vpow /= 0 ) THEN - A(i,j) = A(i,j) * v**vpow - END IF - - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,upow) - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,vpow) - END DO - END DO - -! ALLOCATE( element % BasisFunctions(MaxDeg2,MaxDeg2) ) - -!------------------------------------------------------------------------------ -! 3D volume elements -!------------------------------------------------------------------------------ - ELSE - - DO i = 1,n - u = element % NodeU(i) - v = element % NodeV(i) - w = element % NodeW(i) - DO j = 1,n - k = BasisTerms(j) - 1 - upow = MOD( k,MaxDeg ) - wpow = k / MaxDeg2 - vpow = MOD( k / MaxDeg, MaxDeg ) - - IF ( upow == 0 ) THEN - A(i,j) = 1 - ELSE - A(i,j) = u**upow - END IF - - IF ( vpow /= 0 ) THEN - A(i,j) = A(i,j) * v**vpow - END IF - - IF ( wpow /= 0 ) THEN - A(i,j) = A(i,j) * w**wpow - END IF - - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,upow) - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,vpow) - element % BasisFunctionDegree = MAX(element % BasisFunctionDegree,wpow) - END DO - END DO - -! ALLOCATE( element % BasisFunctions(MaxDeg3,MaxDeg3) ) - END IF - -!------------------------------------------------------------------------------ -! Compute the coefficients of the basis function terms -!------------------------------------------------------------------------------ - CALL InvertMatrix( A,n ) - - IF ( Element % ElementCode == 202 ) THEN - ALLOCATE( Element % BasisFunctions(14) ) - ELSE - ALLOCATE( Element % BasisFunctions(n) ) - END IF - - upow = 0 - vpow = 0 - wpow = 0 - - DO i = 1,n - Element % BasisFunctions(i) % n = n - ALLOCATE( Element % BasisFunctions(i) % p(n) ) - ALLOCATE( Element % BasisFunctions(i) % q(n) ) - ALLOCATE( Element % BasisFunctions(i) % r(n) ) - ALLOCATE( Element % BasisFunctions(i) % Coeff(n) ) - - DO j = 1,n - k = BasisTerms(j) - 1 - - SELECT CASE( Element % DIMENSION ) - CASE(1) - upow = k - CASE(2) - vpow = k / MaxDeg - upow = MOD(k,MaxDeg) - CASE(3) - upow = MOD( k,MaxDeg ) - wpow = k / MaxDeg2 - vpow = MOD( k / MaxDeg, MaxDeg ) - END SELECT - - Element % BasisFunctions(i) % p(j) = upow - Element % BasisFunctions(i) % q(j) = vpow - Element % BasisFunctions(i) % r(j) = wpow - Element % BasisFunctions(i) % Coeff(j) = A(j,i) - END DO - END DO - - DEALLOCATE( A ) - - IF ( Element % ElementCode == 202 ) THEN - ALLOCATE( A(14,14) ) - A = 0 - CALL Compute1DPBasis( A,14 ) - - DO i=3,14 - ALLOCATE( Element % BasisFunctions(i) % p(i) ) - ALLOCATE( Element % BasisFunctions(i) % q(i) ) - ALLOCATE( Element % BasisFunctions(i) % r(i) ) - ALLOCATE( Element % BasisFunctions(i) % Coeff(i) ) - - k = 0 - DO j=1,i - IF ( A(i,j) /= 0.0d0 ) THEN - k = k + 1 - Element % BasisFunctions(i) % p(k) = j-1 - Element % BasisFunctions(i) % q(k) = 0 - Element % BasisFunctions(i) % r(k) = 0 - Element % BasisFunctions(i) % Coeff(k) = A(i,j) - END IF - END DO - Element % BasisFunctions(i) % n = k - END DO - DEALLOCATE( A ) - END IF - -!------------------------------------------------------------------------------ - - SELECT CASE( Element % ElementCode / 100 ) - CASE(3) - Element % NumberOfEdges = 3 - CASE(4) - Element % NumberOfEdges = 4 - CASE(5) - Element % NumberOfFaces = 4 - Element % NumberOfEdges = 6 - CASE(6) - Element % NumberOfFaces = 5 - Element % NumberOfEdges = 8 - CASE(7) - Element % NumberOfFaces = 5 - Element % NumberOfEdges = 9 - CASE(8) - Element % NumberOfFaces = 6 - Element % NumberOfEdges = 12 - END SELECT - - END IF ! type >= 200 - -!------------------------------------------------------------------------------ -! And finally add the element description to the global list of types -!------------------------------------------------------------------------------ - IF ( .NOT.TypeListInitialized ) THEN - ALLOCATE( ElementTypeList ) - ElementTypeList = element - TypeListInitialized = .TRUE. - NULLIFY( ElementTypeList % NextElementType ) - ELSE - ALLOCATE( temp ) - temp = element - temp % NextElementType => ElementTypeList - ElementTypeList => temp - END IF - -!------------------------------------------------------------------------------ - -CONTAINS - - -!------------------------------------------------------------------------------ -!> Subroutine to compute 1D P-basis from Legendre polynomials. -!------------------------------------------------------------------------------ - SUBROUTINE Compute1DPBasis( Basis,n ) -!------------------------------------------------------------------------------ - INTEGER :: n - REAL(KIND=dp) :: Basis(:,:) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s,P(n+1),Q(n),P0(n),P1(n+1) - INTEGER :: i,j,k,np,info - -!------------------------------------------------------------------------------ - - IF ( n <= 1 ) THEN - Basis(1,1) = 1.0d0 - RETURN - END IF -!------------------------------------------------------------------------------ -! Compute coefficients of n:th Legendre polynomial from the recurrence: -! -! (i+1)P_{i+1}(x) = (2i+1)*x*P_i(x) - i*P_{i-1}(x), P_{0} = 1; P_{1} = x; -! -! CAVEAT: Computed coefficients inaccurate for n > ~15 -!------------------------------------------------------------------------------ - P = 0 - P0 = 0 - P1 = 0 - P0(1) = 1 - P1(1) = 1 - P1(2) = 0 - - Basis(1,1) = 0.5d0 - Basis(1,2) = -0.5d0 - - Basis(2,1) = 0.5d0 - Basis(2,2) = 0.5d0 - - DO k=2,n - IF ( k > 2 ) THEN - s = SQRT( (2.0d0*(k-1)-1) / 2.0d0 ) - DO j=1,k-1 - Basis(k,k-j+1) = s * P0(j) / (k-j) - Basis(k,1) = Basis(k,1) - s * P0(j)*(-1)**(j+1) / (k-j) - END DO - END IF - - i = k - 1 - P(1:i+1) = (2*i+1) * P1(1:i+1) / (i+1) - P(3:i+2) = P(3:i+2) - i*P0(1:i) / (i+1) - P0(1:i+1) = P1(1:i+1) - P1(1:i+2) = P(1:i+2) - END DO -!-------------------------------------------------------------------------- - END SUBROUTINE Compute1DPBasis -!-------------------------------------------------------------------------- - - END SUBROUTINE AddElementDescription -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Read the element description input file and add the element types to a -!> global list. The file is assumed to be found under the name -!> $ELMER_HOME/lib/elements.def -!> This is the first routine the user of the element utilities should call -!> in his/her code. -!------------------------------------------------------------------------------ - SUBROUTINE InitializeElementDescriptions() -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - CHARACTER(LEN=:), ALLOCATABLE :: str - CHARACTER(LEN=MAX_STRING_LEN) :: tstr,elmer_home - - INTEGER :: k, n - INTEGER, DIMENSION(MaxDeg3) :: BasisTerms - - TYPE(ElementType_t) :: element - - LOGICAL :: gotit, fexist -!------------------------------------------------------------------------------ -! PRINT*,' ' -! PRINT*,'----------------------------------------------' -! PRINT*,'Reading element definition file: elements.def' -! PRINT*,'----------------------------------------------' - - - ! - ! Add connectivity element types: - ! ------------------------------- - BasisTerms = 0 - element % GaussPoints = 0 - element % GaussPoints0 = 0 - element % GaussPoints2 = 0 - element % StabilizationMK = 0 - NULLIFY( element % NodeU ) - NULLIFY( element % NodeV ) - NULLIFY( element % NodeW ) - DO k=3,64 - element % NumberOfNodes = k - element % ElementCode = 100 + k - CALL AddElementDescription( element,BasisTerms ) - END DO - - ! then the rest of them.... - !-------------------------- -#ifdef USE_ISO_C_BINDINGS - tstr = 'ELMER_LIB' -#else - tstr = 'ELMER_LIB'//CHAR(0) -#endif - CALL envir( tstr,elmer_home,k ) - - fexist = .FALSE. - IF ( k > 0 ) THEN - WRITE( tstr, '(a,a)' ) elmer_home(1:k),'/elements.def' - INQUIRE(FILE=TRIM(tstr), EXIST=fexist) - END IF - IF (.NOT. fexist) THEN -#ifdef USE_ISO_C_BINDINGS - tstr = 'ELMER_HOME' -#else - tstr = 'ELMER_HOME'//CHAR(0) -#endif - CALL envir( tstr,elmer_home,k ) - IF ( k > 0 ) THEN - WRITE( tstr, '(a,a)' ) elmer_home(1:k),& -'/share/elmersolver/lib/elements.def' - INQUIRE(FILE=TRIM(tstr), EXIST=fexist) - END IF - IF ((.NOT. fexist) .AND. k > 0) THEN - WRITE( tstr, '(a,a)' ) elmer_home(1:k),& - '/elements.def' - INQUIRE(FILE=TRIM(tstr), EXIST=fexist) - END IF - END IF - IF (.NOT. fexist) THEN - CALL GetSolverHome(elmer_home, n) - WRITE(tstr, '(a,a)') elmer_home(1:n), & - '/lib/elements.def' - INQUIRE(FILE=TRIM(tstr), EXIST=fexist) - END IF - IF (.NOT. fexist) THEN - CALL Fatal('InitializeElementDescriptions', & - 'elements.def not found') - END IF - - OPEN( 1,FILE=TRIM(tstr), STATUS='OLD' ) - - ALLOCATE(CHARACTER(MAX_STRING_LEN)::str) - DO WHILE( ReadAndTrim(1,str) ) - - IF ( SEQL(str, 'element') ) THEN - - BasisTerms = 0 - - NULLIFY( element % NodeU ) - NULLIFY( element % NodeV ) - NULLIFY( element % NodeW ) - - gotit = .FALSE. - DO WHILE( ReadAndTrim(1,str) ) - - IF ( SEQL(str, 'dimension') ) THEN - READ( str(10:), * ) element % DIMENSION - - ELSE IF ( SEQL(str, 'code') ) THEN - READ( str(5:), * ) element % ElementCode - - ELSE IF ( SEQL(str, 'nodes') ) THEN - READ( str(6:), * ) element % NumberOfNodes - - ELSE IF ( SEQL(str, 'node u') ) THEN - ALLOCATE( element % NodeU(element % NumberOfNodes) ) - READ( str(7:), * ) (element % NodeU(k),k=1,element % NumberOfNodes) - - ELSE IF ( SEQL(str, 'node v') ) THEN - ALLOCATE( element % NodeV(element % NumberOfNodes) ) - READ( str(7:), * ) (element % NodeV(k),k=1,element % NumberOfNodes) - - ELSE IF ( SEQL(str, 'node w') ) THEN - ALLOCATE( element % NodeW(element % NumberOfNodes ) ) - READ( str(7:), * ) (element % NodeW(k),k=1,element % NumberOfNodes) - - ELSE IF ( SEQL(str, 'basis') ) THEN - READ( str(6:), * ) (BasisTerms(k),k=1,element % NumberOfNodes) - - ELSE IF ( SEQL(str, 'stabilization') ) THEN - READ( str(14:), * ) element % StabilizationMK - - ELSE IF ( SEQL(str, 'gauss points') ) THEN - - Element % GaussPoints2 = 0 - READ( str(13:), *,END=10 ) element % GaussPoints,& - element % GaussPoints2, element % GaussPoints0 - -10 CONTINUE - - IF ( Element % GaussPoints2 <= 0 ) & - Element % GaussPoints2 = Element % GaussPoints - - IF ( Element % GaussPoints0 <= 0 ) & - Element % GaussPoints0 = Element % GaussPoints - - ELSE IF ( str == 'end element' ) THEN - gotit = .TRUE. - EXIT - END IF - END DO - - IF ( gotit ) THEN - Element % StabilizationMK = 0.0d0 - IF ( .NOT.ASSOCIATED( element % NodeV ) ) THEN - ALLOCATE( element % NodeV(element % NumberOfNodes) ) - element % NodeV = 0.0d0 - END IF - - IF ( .NOT.ASSOCIATED( element % NodeW ) ) THEN - ALLOCATE( element % NodeW(element % NumberOfNodes) ) - element % NodeW = 0.0d0 - END IF - - CALL AddElementDescription( element,BasisTerms ) - ELSE - IF ( ASSOCIATED( element % NodeU ) ) DEALLOCATE( element % NodeU ) - IF ( ASSOCIATED( element % NodeV ) ) DEALLOCATE( element % NodeV ) - IF ( ASSOCIATED( element % NodeW ) ) DEALLOCATE( element % NodeW ) - END IF - END IF - END DO - - CLOSE(1) -!------------------------------------------------------------------------------ - END SUBROUTINE InitializeElementDescriptions -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element type code return pointer to the corresponding element type -!> structure. -!------------------------------------------------------------------------------ - FUNCTION GetElementType( code,CompStabFlag ) RESULT(element) -!------------------------------------------------------------------------------ - INTEGER :: code - LOGICAL, OPTIONAL :: CompStabFlag - TYPE(ElementType_t), POINTER :: element -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(Nodes_t) :: Nodes - INTEGER :: sdim - TYPE(Element_t), POINTER :: Elm -!------------------------------------------------------------------------------ - element => ElementTypeList - - DO WHILE( ASSOCIATED(element) ) - IF ( code == element % ElementCode ) EXIT - element => element % NextElementType - END DO - - IF ( .NOT. ASSOCIATED( element ) ) THEN - WRITE( message, * ) & - 'Element type code ',code,' not found. Ignoring element.' - CALL Warn( 'GetElementType', message ) - RETURN - END IF - - IF ( PRESENT( CompStabFlag ) ) THEN - IF ( .NOT. CompStabFlag ) RETURN - END IF - - IF ( Element % StabilizationMK == 0.0d0 ) THEN - ALLOCATE( Elm ) - Elm % TYPE => element - Elm % BDOFs = 0 - Elm % DGDOFs = 0 - NULLIFY( Elm % PDefs ) - NULLIFY( Elm % DGIndexes ) - NULLIFY( Elm % EdgeIndexes ) - NULLIFY( Elm % FaceIndexes ) - NULLIFY( Elm % BubbleIndexes ) - Nodes % x => Element % NodeU - Nodes % y => Element % NodeV - Nodes % z => Element % NodeW - - sdim = CurrentModel % Dimension - CurrentModel % Dimension = Element % Dimension - CALL StabParam( Elm, Nodes, Element % NumberOfNodes, & - Element % StabilizationMK ) - CurrentModel % Dimension = sdim - - DEALLOCATE(Elm) - END IF - - END FUNCTION GetElementType -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Compute convection diffusion equation stab. parameter for each and every -!> element of the model by solving the largest eigenvalue of -! -!> Lu = \lambda Gu, -! -!> L = (\nablda^2 u,\nabla^ w), G = (\nabla u,\nabla w) -!------------------------------------------------------------------------------ - SUBROUTINE StabParam(Element,Nodes,n,mK,hK,UseLongEdge) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), POINTER :: Element - INTEGER :: n - TYPE(Nodes_t) :: Nodes - REAL(KIND=dp) :: mK - REAL(KIND=dp), OPTIONAL :: hK - LOGICAL, OPTIONAL :: UseLongEdge -!------------------------------------------------------------------------------ - INTEGER :: info,p,q,i,j,t,dim - REAL(KIND=dp) :: EIGR(n),EIGI(n),Beta(n),s,ddp(3),ddq(3),dNodalBasisdx(n,n,3) - REAL(KIND=dp) :: u,v,w,L(n-1,n-1),G(n-1,n-1),Work(16*n) - REAL(KIND=dp) :: Basis(n),dBasisdx(n,3),ddBasisddx(n,3,3),detJ - - LOGICAL :: stat - TYPE(GaussIntegrationPoints_t) :: IntegStuff - - IF ( Element % TYPE % BasisFunctionDegree <= 1 ) THEN - SELECT CASE( Element % TYPE % ElementCode ) - CASE( 202, 303, 404, 504, 605, 706 ) - mK = 1.0d0 / 3.0d0 - CASE( 808 ) - mK = 1.0d0 / 6.0d0 - END SELECT - IF ( PRESENT( hK ) ) hK = ElementDiameter( Element, Nodes, UseLongEdge) - RETURN - END IF - - dNodalBasisdx = 0._dp - DO p=1,n - u = Element % TYPE % NodeU(p) - v = Element % TYPE % NodeV(p) - w = Element % TYPE % NodeW(p) - stat = ElementInfo( Element, Nodes, u,v,w, detJ, Basis, dBasisdx ) - dNodalBasisdx(1:n,p,:) = dBasisdx(1:n,:) - END DO - - dim = CoordinateSystemDimension() - IntegStuff = GaussPoints( Element ) - L = 0.0d0 - G = 0.0d0 - DO t=1,IntegStuff % n - u = IntegStuff % u(t) - v = IntegStuff % v(t) - w = IntegStuff % w(t) - - stat = ElementInfo( Element,Nodes,u,v,w,detJ,Basis, & - dBasisdx ) - - s = detJ * IntegStuff % s(t) - - DO p=2,n - DO q=2,n - ddp = 0.0d0 - ddq = 0.0d0 - DO i=1,dim - G(p-1,q-1) = G(p-1,q-1) + s * dBasisdx(p,i) * dBasisdx(q,i) - ddp(i) = ddp(i) + SUM( dNodalBasisdx(p,1:n,i) * dBasisdx(1:n,i) ) - ddq(i) = ddq(i) + SUM( dNodalBasisdx(q,1:n,i) * dBasisdx(1:n,i) ) - END DO - L(p-1,q-1) = L(p-1,q-1) + s * SUM(ddp) * SUM(ddq) - END DO - END DO - END DO - - IF ( ALL(ABS(L) < AEPS) ) THEN - mK = 1.0d0 / 3.0d0 - IF ( PRESENT(hK) ) THEN - hK = ElementDiameter( Element,Nodes,UseLongEdge) - END IF - RETURN - END IF - - - CALL DSYGV( 1,'N','U',n-1,L,n-1,G,n-1,EIGR,Work,12*n,info ) - mK = EIGR(n-1) - - IF ( mK < 10*AEPS ) THEN - mK = 1.0d0 / 3.0d0 - IF ( PRESENT(hK) ) THEN - hK = ElementDiameter( Element,Nodes,UseLongEdge ) - END IF - RETURN - END IF - - IF ( PRESENT( hK ) ) THEN - hK = SQRT( 2.0d0 / (mK * Element % TYPE % StabilizationMK) ) - mK = MIN( 1.0d0 / 3.0d0, Element % TYPE % StabilizationMK ) - ELSE - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(2,4,8) - mK = 4 * mK - END SELECT - mK = MIN( 1.0d0/3.0d0, 2/mK ) - END IF - -!------------------------------------------------------------------------------ - END SUBROUTINE StabParam -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Given element structure return value of a quantity x given at element nodes -!> at local coordinate point u inside the element. Element basis functions are -!> used to compute the value. This is for 1D elements, and shouldn't probably -!> be called directly by the user but through the wrapper routine -!> InterpolateInElement. -!------------------------------------------------------------------------------ - FUNCTION InterpolateInElement1D( element,x,u ) RESULT(y) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the value - REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity whose value we want to know - REAL(KIND=dp) :: y !< value of the quantity y = x(u) -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s - INTEGER :: i,j,k,n - TYPE(ElementType_t), POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - - elt => element % TYPE - k = Elt % NumberOfNodes - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n=1,k - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) - END DO - y = y + s * x(n) - END IF - END DO - END FUNCTION InterpolateInElement1D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalBasisFunctions1D( y,element,u ) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the value - REAL(KIND=dp) :: y(:) !< value of the quantity y = x(u) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s - INTEGER :: i,n - TYPE(ElementType_t), POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - DO n=1,Elt % NumberOfNodes - p => BasisFunctions(n) % p - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) - END DO - y(n) = s - END DO - END SUBROUTINE NodalBasisFunctions1D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate of a quantity x given at element nodes at local -!> coordinate point u inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivative1D( element,x,u ) RESULT(y) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the partial derivative - REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity whose partial derivative we want to know - REAL(KIND=dp) :: y !< value of the quantity y = @x/@u -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - INTEGER :: i,j,k,n,l - TYPE(ElementType_t), POINTER :: elt - REAL(KIND=dp) :: s - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - elt => element % TYPE - k = Elt % NumberOfNodes - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n=1,k - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - IF ( p(i) >= 1 ) THEN - s = s + p(i) * Coeff(i) * u**(p(i)-1) - END IF - END DO - y = y + s * x(n) - END IF - END DO - END FUNCTION FirstDerivative1D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalFirstDerivatives1D( y,element,u ) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: u !< Point at which to evaluate the partial derivative - REAL(KIND=dp) :: y(:,:) !< value of the quantity y = @x/@u - TYPE(Element_t) :: element !< element structure -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(ElementType_t), POINTER :: elt - INTEGER :: i,n - REAL(KIND=dp) :: s - - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - DO n=1, Elt % NumberOfNodes - p => BasisFunctions(n) % p - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - IF (p(i)>=1) s = s + p(i)*Coeff(i)*u**(p(i)-1) - END DO - y(n,1) = s - END DO - END SUBROUTINE NodalFirstDerivatives1D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the second partial derivative with -!> respect to local coordinate of a quantity x given at element nodes at local -!> coordinate point u inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION SecondDerivatives1D( element,x,u ) RESULT(y) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the partial derivative - REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity whose partial derivative we want to know - REAL(KIND=dp) :: y !< value of the quantity y = @x/@u -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: usum - INTEGER :: i,j,k,n - TYPE(ElementType_t), POINTER :: elt - INTEGER, POINTER :: p(:),q(:) - REAL(KIND=dp), POINTER :: Coeff(:) - REAL(KIND=dp) :: s - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - elt => element % TYPE - k = Elt % NumberOfNodes - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n=1,k - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - IF ( p(i) >= 2 ) THEN - s = s + p(i) * (p(i)-1) * Coeff(i) * u**(p(i)-2) - END IF - END DO - y = y + s * x(n) - END IF - END DO - END FUNCTION SecondDerivatives1D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of a quantity x given at element nodes -!> at local coordinate point (u,vb) inside the element. Element basis functions -!> are used to compute the value.This is for 2D elements, and shouldn't probably -!> be called directly by the user but through the wrapper routine -!> InterpolateInElement. -!------------------------------------------------------------------------------ - FUNCTION InterpolateInElement2D( element,x,u,v ) RESULT(y) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the partial derivative - REAL(KIND=dp) :: v !< Point at which to evaluate the partial derivative - REAL(KIND=dp), DIMENSION(:) :: x !< Nodal values of the quantity whose partial derivative we want to know - REAL(KIND=dp) :: y !< value of the quantity y = x(u,v) -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s,t - - INTEGER :: i,j,k,m,n - - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) * v**q(i) - END DO - y = y + s*x(n) - END IF - END DO - - END FUNCTION InterpolateInElement2D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalBasisFunctions2D( y,element,u,v ) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y(:) !< The values of the reference element basis - TYPE(Element_t) :: element !< element structure - REAL(KIND=dp) :: u !< Point at which to evaluate the value - REAL(KIND=dp) :: v !< Point at which to evaluate the value -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s - INTEGER :: i,n - TYPE(ElementType_t), POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: ult(0:6), vlt(0:6) - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - ult(0) = 1 - ult(1) = u - - vlt(0) = 1 - vlt(1) = v - - DO i=2,elt % BasisFunctionDegree - ult(i) = u**i - vlt(i) = v**i - END DO - - DO n=1,Elt % NumberOfNodes - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - s = s + Coeff(i)*ult(p(i))*vlt(q(i)) - END DO - y(n) = s - END DO - END SUBROUTINE NodalBasisFunctions2D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate u of i quantity x given at element nodes at local -!> coordinate point u,v inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivativeInU2D( element,x,u,v ) RESULT(y) -!------------------------------------------------------------------------------ -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v)/@u -! -!****************************************************************************** - ! - ! Return first partial derivative in u of a quantity x at point u,v - ! - ! - ! - - TYPE(Element_t) :: element - - REAL(KIND=dp) :: u,v - REAL(KIND=dp), DIMENSION(:) :: x - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: y,s,t - - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - INTEGER :: i,j,k,m,n - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( p(i) >= 1 ) THEN - s = s + p(i) * Coeff(i) * u**(p(i)-1) * v**q(i) - END IF - END DO - y = y + s*x(n) - END IF - END DO - - END FUNCTION FirstDerivativeInU2D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate v of i quantity x given at element nodes at local -!> coordinate point u,v inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivativeInV2D( element,x,u,v ) RESULT(y) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v)/@v -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in v of a quantity x at point u,v - ! - ! - ! - TYPE(Element_t) :: element - - REAL(KIND=dp), DIMENSION(:) :: x - REAL(KIND=dp) :: u,v - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y,s,t - - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - INTEGER :: i,j,k,m,n - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( q(i) >= 1 ) THEN - s = s + q(i) * Coeff(i) * u**p(i) * v**(q(i)-1) - END IF - END DO - y = y + s*x(n) - END IF - END DO - - END FUNCTION FirstDerivativeInV2D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalFirstDerivatives2D( y,element,u,v ) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v)/@u -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in u of a quantity x at point u,v - ! - ! - ! - - TYPE(Element_t) :: element - REAL(KIND=dp) :: u,v,y(:,:) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: s,t - - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - INTEGER :: i,n - - REAL(KIND=dp) :: ult(0:6), vlt(0:6) - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - ult(0) = 1 - ult(1) = u - - vlt(0) = 1 - vlt(1) = v - - DO i=2,elt % BasisFunctionDegree - ult(i) = u**i - vlt(i) = v**i - END DO - - - DO n = 1,elt % NumberOfNodes - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - t = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF (p(i)>=1) s = s + p(i)*Coeff(i)*ult(p(i)-1)*vlt(q(i)) - IF (q(i)>=1) t = t + q(i)*Coeff(i)*ult(p(i))*vlt(q(i)-1) - END DO - y(n,1) = s - y(n,2) = t - END DO - - END SUBROUTINE NodalFirstDerivatives2D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the second partial derivatives with -!> respect to local coordinates of a quantity x given at element nodes at local -!> coordinate point u,v inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION SecondDerivatives2D( element,x,u,v ) RESULT(ddx) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivatives we want to know -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: s -! value of the quantity s = @^2x(u,v)/@v^2 -! -!------------------------------------------------------------------------------ - - TYPE(Element_t) :: element - - REAL(KIND=dp), DIMENSION(:) :: x - REAL(KIND=dp) :: u,v - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), DIMENSION (2,2) :: ddx - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - REAL(KIND=dp) :: s,t - INTEGER, POINTER :: p(:),q(:) - REAL(KIND=dp), POINTER :: Coeff(:) - - INTEGER :: i,j,k,n,m - -!------------------------------------------------------------------------------ - elt => element % TYPE - k = elt % NumberOfNodes - BasisFunctions => elt % BasisFunctions - - ddx = 0.0d0 - - DO n = 1,k - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - Coeff => BasisFunctions(n) % Coeff -!------------------------------------------------------------------------------ -! @^2x/@u^2 -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1, BasisFunctions(n) % n - IF ( p(i) >= 2 ) THEN - s = s + p(i) * (p(i)-1) * Coeff(i) * u**(p(i)-2) * v**q(i) - END IF - END DO - ddx(1,1) = ddx(1,1) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@u@v -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1, BasisFunctions(n) % n - IF ( p(i) >= 1 .AND. q(i) >= 1 ) THEN - s = s + p(i) * q(i) * Coeff(i) * u**(p(i)-1) * v**(q(i)-1) - END IF - END DO - ddx(1,2) = ddx(1,2) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@v^2 -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1, BasisFunctions(n) % n - IF ( q(i) >= 2 ) THEN - s = s + q(i) * (q(i)-1) * Coeff(i) * u**p(i) * v**(q(i)-2) - END IF - END DO - ddx(2,2) = ddx(2,2) + s*x(n) - END IF - END DO - - ddx(2,1) = ddx(1,2) - - END FUNCTION SecondDerivatives2D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of a quantity x given at element nodes -!> at local coordinate point (u,v,w) inside the element. Element basis functions -!> are used to compute the value. This is for 3D elements, and shouldn't probably -!> be called directly by the user but through the wrapper routine -!> InterpolateInElement. -!------------------------------------------------------------------------------ - FUNCTION InterpolateInElement3D( element,x,u,v,w ) RESULT(y) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose value we want to know -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the value -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = x(u,v,w) -! -!------------------------------------------------------------------------------ - ! - ! Return value of a quantity x at point u,v,w - ! - TYPE(Element_t) :: element - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp), DIMENSION(:) :: x -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y - - TYPE(ElementType_t),POINTER :: elt - - INTEGER :: i,j,k,l,n,m - - REAL(KIND=dp) :: s,t - INTEGER, POINTER :: p(:),q(:), r(:) - REAL(KIND=dp), POINTER :: Coeff(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - - elt => element % TYPE - l = elt % BasisFunctionDegree - BasisFunctions => elt % BasisFunctions - - IF ( Elt % ElementCode == 605 ) THEN - s = 0.0d0 - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( (1-u) * (1-v) - w + u*v*w * s ) / 4 - y = y + x(2) * ( (1+u) * (1-v) - w - u*v*w * s ) / 4 - y = y + x(3) * ( (1+u) * (1+v) - w + u*v*w * s ) / 4 - y = y + x(4) * ( (1-u) * (1+v) - w - u*v*w * s ) / 4 - y = y + x(5) * w - RETURN - ELSE IF ( Elt % ElementCode == 613 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * (-u-v-1) * ( (1-u) * (1-v) - w + u*v*w * s ) / 4 - y = y + x(2) * ( u-v-1) * ( (1+u) * (1-v) - w - u*v*w * s ) / 4 - y = y + x(3) * ( u+v-1) * ( (1+u) * (1+v) - w + u*v*w * s ) / 4 - y = y + x(4) * (-u+v-1) * ( (1-u) * (1+v) - w - u*v*w * s ) / 4 - y = y + x(5) * w*(2*w-1) - y = y + x(6) * (1+u-w)*(1-u-w)*(1-v-w) * s / 2 - y = y + x(7) * (1+v-w)*(1-v-w)*(1+u-w) * s / 2 - y = y + x(8) * (1+u-w)*(1-u-w)*(1+v-w) * s / 2 - y = y + x(9) * (1+v-w)*(1-v-w)*(1-u-w) * s / 2 - y = y + x(10) * w * (1-u-w) * (1-v-w) * s - y = y + x(11) * w * (1+u-w) * (1-v-w) * s - y = y + x(12) * w * (1+u-w) * (1+v-w) * s - y = y + x(13) * w * (1-u-w) * (1+v-w) * s - RETURN - END IF - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - s = s + Coeff(i) * u**p(i) * v**q(i) * w**r(i) - END DO - y = y + s*x(n) - END IF - END DO -!------------------------------------------------------------------------------ - END FUNCTION InterpolateInElement3D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalBasisFunctions3D( y,element,u,v,w ) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: u -! INPUT: Point at which to evaluate the value -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = x(u) -! -!------------------------------------------------------------------------------ - - TYPE(Element_t) :: element - REAL(KIND=dp) :: u,v,w,y(:) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: s - - INTEGER :: i,n - - TYPE(ElementType_t), POINTER :: elt - - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:),r(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: ult(0:6), vlt(0:6), wlt(0:6) - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - ult(0) = 1 - ult(1) = u - - vlt(0) = 1 - vlt(1) = v - - wlt(0) = 1 - wlt(1) = w - - DO i=2,elt % BasisFunctionDegree - ult(i) = u**i - vlt(i) = v**i - wlt(i) = w**i - END DO - - DO n=1,Elt % NumberOfNodes - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i=1,BasisFunctions(n) % n - s = s + Coeff(i)*ult(p(i))*vlt(q(i))*wlt(r(i)) - END DO - y(n) = s - END DO - END SUBROUTINE NodalBasisFunctions3D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate u of a quantity x given at element nodes at -!> local coordinate point u,v,w inside the element. Element basis functions -!> are used to compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivativeInU3D( element,x,u,v,w ) RESULT(y) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v,w)/@u -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in u of a quantity x at point u,v,w - ! - - TYPE(Element_t) :: element - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp), DIMENSION(:) :: x - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y - - TYPE(ElementType_t),POINTER :: elt - INTEGER :: i,j,k,l,n,m - - REAL(KIND=dp) :: s,t - - INTEGER, POINTER :: p(:),q(:), r(:) - REAL(KIND=dp), POINTER :: Coeff(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - elt => element % TYPE - l = elt % BasisFunctionDegree - BasisFunctions => elt % BasisFunctions - -IF ( Elt % ElementCode == 605 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( -(1-v) + v*w * s ) / 4 - y = y + x(2) * ( (1-v) - v*w * s ) / 4 - y = y + x(3) * ( (1+v) + v*w * s ) / 4 - y = y + x(4) * ( -(1+v) - v*w * s ) / 4 - RETURN -ELSE IF ( Elt % ElementCode == 613 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( -( (1-u) * (1-v) - w + u*v*w * s ) + & - (-u-v-1) * ( -(1-v) + v*w * s ) ) / 4 - - y = y + x(2) * ( ( (1+u) * (1-v) - w - u*v*w * s ) + & - ( u-v-1) * ( (1-v) - v*w * s ) ) / 4 - - y = y + x(3) * ( ( (1+u) * (1+v) - w + u*v*w * s ) + & - ( u+v-1) * ( (1+v) + v*w * s ) ) / 4 - - y = y + x(4) * ( -( (1-u) * (1+v) - w - u*v*w * s ) + & - (-u+v-1) * ( -(1+v) - v*w * s ) ) / 4 - - y = y + x(5) * 0.0d0 - - y = y + x(6) * ( (1-u-w)*(1-v-w) - (1+u-w)*(1-v-w) ) * s / 2 - y = y + x(7) * ( (1+v-w)*(1-v-w) ) * s / 2 - y = y + x(8) * ( (1-u-w)*(1+v-w) - (1+u-w)*(1+v-w) ) * s / 2 - y = y + x(9) * ( -(1+v-w)*(1-v-w) ) * s / 2 - - y = y - x(10) * w * (1-v-w) * s - y = y + x(11) * w * (1-v-w) * s - y = y + x(12) * w * (1+v-w) * s - y = y - x(13) * w * (1+v-w) * s - - RETURN -END IF - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( p(i) >= 1 ) THEN - s = s + p(i) * Coeff(i) * u**(p(i)-1) * v**q(i) * w**r(i) - END IF - END DO - y = y + s*x(n) - END IF - END DO -!------------------------------------------------------------------------------ - END FUNCTION FirstDerivativeInU3D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to local coordinate v of a quantity x given at element nodes at -!> local coordinate point u,v,w inside the element. Element basis functions -!> are used to compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivativeInV3D( element,x,u,v,w ) RESULT(y) -!------------------------------------------------------------------------------ -! -! DESCRIPTION: -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v,w)/@v -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in v of a quantity x at point u,v,w - ! - - TYPE(Element_t) :: element - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp), DIMENSION(:) :: x - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y - - TYPE(ElementType_t),POINTER :: elt - - INTEGER :: i,j,k,l,n,m - - REAL(KIND=dp) :: s,t - - INTEGER, POINTER :: p(:),q(:), r(:) - REAL(KIND=dp), POINTER :: Coeff(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - elt => element % TYPE - l = elt % BasisFunctionDegree - BasisFunctions => elt % BasisFunctions - -IF ( Elt % ElementCode == 605 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( -(1-u) + u*w * s ) / 4 - y = y + x(2) * ( -(1+u) - u*w * s ) / 4 - y = y + x(3) * ( (1+u) + u*w * s ) / 4 - y = y + x(4) * ( (1-u) - u*w * s ) / 4 - - RETURN -ELSE IF ( Elt % ElementCode == 613 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( -( (1-u) * (1-v) - w + u*v*w * s ) + & - (-u-v-1) * ( -(1-u) + u*w * s ) ) / 4 - - y = y + x(2) * ( -( (1+u) * (1-v) - w - u*v*w * s ) + & - ( u-v-1) * ( -(1+u) - u*w * s ) ) / 4 - - y = y + x(3) * ( ( (1+u) * (1+v) - w + u*v*w * s ) + & - ( u+v-1) * ( (1+u) + u*w * s ) ) / 4 - - y = y + x(4) * ( ( (1-u) * (1+v) - w - u*v*w * s ) + & - (-u+v-1) * ( (1-u) - u*w * s ) ) / 4 - - y = y + x(5) * 0.0d0 - - y = y - x(6) * (1+u-w)*(1-u-w) * s / 2 - y = y + x(7) * ( (1-v-w)*(1+u-w) - (1+v-w)*(1+u-w) ) * s / 2 - y = y + x(8) * (1+u-w)*(1-u-w) * s / 2 - y = y + x(9) * ( (1-v-w)*(1-u-w) - (1+v-w)*(1-u-w) ) * s / 2 - - y = y - x(10) * w * (1-u-w) * s - y = y - x(11) * w * (1+u-w) * s - y = y + x(12) * w * (1+u-w) * s - y = y + x(13) * w * (1-u-w) * s - RETURN -END IF - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( q(i) >= 1 ) THEN - s = s + q(i) * Coeff(i) * u**p(i) * v**(q(i)-1) * w**r(i) - END IF - END DO - y = y + s*x(n) - END IF - END DO - END FUNCTION FirstDerivativeInV3D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivatives with -!> respect to local coordinate w of a quantity x given at element nodes at -!> local coordinate point u,v,w inside the element. Element basis functions -!> are used to compute the value. -!------------------------------------------------------------------------------ - FUNCTION FirstDerivativeInW3D( element,x,u,v,w ) RESULT(y) -!------------------------------------------------------------------------------ -! -! DESCRIPTION: -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v,w)/@w -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in u of a quantity x at point u,v,w - ! - ! - - TYPE(Element_t) :: element - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp), DIMENSION(:) :: x - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: y - - TYPE(ElementType_t),POINTER :: elt - INTEGER :: i,j,k,l,n,m - - REAL(KIND=dp) :: s,t - - INTEGER, POINTER :: p(:),q(:), r(:) - REAL(KIND=dp), POINTER :: Coeff(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) -!------------------------------------------------------------------------------ - elt => element % TYPE - l = elt % BasisFunctionDegree - BasisFunctions => elt % BasisFunctions - -IF ( Elt % ElementCode == 605 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * ( -1 + u*v*s**2 ) / 4 - y = y + x(2) * ( -1 - u*v*s**2 ) / 4 - y = y + x(3) * ( -1 + u*v*s**2 ) / 4 - y = y + x(4) * ( -1 - u*v*s**2 ) / 4 - y = y + x(5) - RETURN -ELSE IF ( Elt % ElementCode == 613 ) THEN - IF ( w == 1 ) w = 1.0d0-1.0d-12 - s = 1.0d0 / (1-w) - - y = 0.0d0 - y = y + x(1) * (-u-v-1) * ( -1 + u*v*s**2 ) / 4 - y = y + x(2) * ( u-v-1) * ( -1 - u*v*s**2 ) / 4 - y = y + x(3) * ( u+v-1) * ( -1 + u*v*s**2 ) / 4 - y = y + x(4) * (-u+v-1) * ( -1 - u*v*s**2 ) / 4 - - y = y + x(5) * (4*w-1) - - y = y + x(6) * ( ( -(1-u-w)*(1-v-w) - (1+u-w)*(1-v-w) - (1+u-w)*(1-u-w) ) * s + & - ( 1+u-w)*(1-u-w)*(1-v-w) * s**2 ) / 2 - - y = y + x(7) * ( ( -(1-v-w)*(1+u-w) - (1+v-w)*(1+u-w) - (1+v-w)*(1-v-w) ) * s + & - ( 1+v-w)*(1-v-w)*(1+u-w) * s**2 ) / 2 - - y = y + x(8) * ( ( -(1-u-w)*(1+v-w) - (1+u-w)*(1+v-w) - (1+u-w)*(1-u-w) ) * s + & - ( 1+u-w)*(1-u-w)*(1+v-w) * s**2 ) / 2 - - y = y + x(9) * ( ( -(1-v-w)*(1-u-w) - (1+v-w)*(1-u-w) - (1+v-w)*(1-v-w) ) * s + & - ( 1+v-w)*(1-v-w)*(1-u-w) * s**2 ) / 2 - - y = y + x(10) * ( ( (1-u-w) * (1-v-w) - w * (1-v-w) - w * (1-u-w) ) * s + & - w * (1-u-w) * (1-v-w) * s**2 ) - - y = y + x(11) * ( ( (1+u-w) * (1-v-w) - w * (1-v-w) - w * (1+u-w) ) * s + & - w * (1+u-w) * (1-v-w) * s**2 ) - - y = y + x(12) * ( ( (1+u-w) * (1+v-w) - w * (1+v-w) - w * (1+u-w) ) * s + & - w * (1+u-w) * (1+v-w) * s**2 ) - - y = y + x(13) * ( ( (1-u-w) * (1+v-w) - w * (1+v-w) - w * (1-u-w) ) * s + & - w * (1-u-w) * (1+v-w) * s**2 ) - RETURN -END IF - - y = 0.0d0 - DO n = 1,elt % NumberOfNodes - IF ( x(n) /= 0.0d0 ) THEN - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( r(i) >= 1 ) THEN - s = s + r(i) * Coeff(i) * u**p(i) * v**q(i) * w**(r(i)-1) - END IF - END DO - y = y + s*x(n) - END IF - END DO -!------------------------------------------------------------------------------ - END FUNCTION FirstDerivativeInW3D -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE NodalFirstDerivatives3D( y,element,u,v,w ) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = @x(u,v)/@u -! -!------------------------------------------------------------------------------ - ! - ! Return first partial derivative in u of a quantity x at point u,v - ! - - TYPE(Element_t) :: element - REAL(KIND=dp) :: u,v,w,y(:,:) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: s,t,z - - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:),q(:),r(:) - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - INTEGER :: i,n - - REAL(KIND=dp) :: ult(0:6), vlt(0:6), wlt(0:6) - - elt => element % TYPE - BasisFunctions => elt % BasisFunctions - - ult(0) = 1 - ult(1) = u - - vlt(0) = 1 - vlt(1) = v - - wlt(0) = 1 - wlt(1) = w - - DO i=2,elt % BasisFunctionDegree - ult(i) = u**i - vlt(i) = v**i - wlt(i) = w**i - END DO - - DO n = 1,elt % NumberOfNodes - p => BasisFunctions(n) % p - q => BasisFunctions(n) % q - r => BasisFunctions(n) % r - Coeff => BasisFunctions(n) % Coeff - - s = 0.0d0 - t = 0.0d0 - z = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF (p(i)>=1) s = s + p(i)*Coeff(i)*ult(p(i)-1)*vlt(q(i))*wlt(r(i)) - IF (q(i)>=1) t = t + q(i)*Coeff(i)*ult(p(i))*vlt(q(i)-1)*wlt(r(i)) - IF (r(i)>=1) z = z + r(i)*Coeff(i)*ult(p(i))*vlt(q(i))*wlt(r(i)-1) - END DO - y(n,1) = s - y(n,2) = t - y(n,3) = z - END DO - END SUBROUTINE NodalFirstDerivatives3D -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the second partial derivatives with -!> respect to local coordinates of i quantity x given at element nodes at local -!> coordinate point u,v inside the element. Element basis functions are used to -!> compute the value. -!------------------------------------------------------------------------------ - FUNCTION SecondDerivatives3D( element,x,u,v,w ) RESULT(ddx) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: x(:) -! INPUT: Nodal values of the quantity whose partial derivatives we want to know -! -! REAL(KIND=dp) :: u,v -! INPUT: Point at which to evaluate the partial derivative -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: s -! value of the quantity s = @^2x(u,v)/@v^2 -! -!------------------------------------------------------------------------------ - ! - ! Return matrix of second partial derivatives. - ! -!------------------------------------------------------------------------------ - - TYPE(Element_t) :: element - - REAL(KIND=dp), DIMENSION(:) :: x - REAL(KIND=dp) :: u,v,w - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp), DIMENSION (3,3) :: ddx - TYPE(BasisFunctions_t), POINTER :: BasisFunctions(:) - - REAL(KIND=dp), POINTER :: Coeff(:) - INTEGER, POINTER :: p(:), q(:), r(:) - - REAL(KIND=dp) :: s - INTEGER :: i,j,k,l,n,m - -!------------------------------------------------------------------------------ - elt => element % TYPE - k = elt % NumberOfNodes - BasisFunctions => elt % BasisFunctions - - ddx = 0.0d0 - - DO n = 1,k - IF ( x(n) /= 0.0d0 ) THEN - p => elt % BasisFunctions(n) % p - q => elt % BasisFunctions(n) % q - r => elt % BasisFunctions(n) % r - Coeff => elt % BasisFunctions(n) % Coeff -!------------------------------------------------------------------------------ -! @^2x/@u^2 -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( p(i) >= 2 ) THEN - s = s + p(i) * (p(i)-1) * Coeff(i) * u**(p(i)-2) * v**q(i) * w**r(i) - END IF - END DO - ddx(1,1) = ddx(1,1) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@u@v -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( p(i) >= 1 .AND. q(i) >= 1 ) THEN - s = s + p(i) * q(i) * Coeff(i) * u**(p(i)-1) * v**(q(i)-1) * w**r(i) - END IF - END DO - ddx(1,2) = ddx(1,2) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@u@w -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 2,k - IF ( p(i) >= 1 .AND. r(i) >= 1 ) THEN - s = s + p(i) * r(i) * Coeff(i) * u**(p(i)-1) * v**q(i) * w**(r(i)-1) - END IF - END DO - ddx(1,3) = ddx(1,3) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@v^2 -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( q(i) >= 2 ) THEN - s = s + q(i) * (q(i)-1) * Coeff(i) * u**p(i) * v**(q(i)-2) * w**r(i) - END IF - END DO - ddx(2,2) = ddx(2,2) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@v@w -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( q(i) >= 1 .AND. r(i) >= 1 ) THEN - s = s + q(i) * r(i) * Coeff(i) * u**p(i) * v**(q(i)-1) * w**(r(i)-1) - END IF - END DO - ddx(2,3) = ddx(2,3) + s*x(n) - -!------------------------------------------------------------------------------ -! @^2x/@w^2 -!------------------------------------------------------------------------------ - s = 0.0d0 - DO i = 1,BasisFunctions(n) % n - IF ( r(i) >= 2 ) THEN - s = s + r(i) * (r(i)-1) * Coeff(i) * u**p(i) * v**q(i) * w**(r(i)-2) - END IF - END DO - ddx(3,3) = ddx(3,3) + s*x(n) - - END IF - END DO - - ddx(2,1) = ddx(1,2) - ddx(3,1) = ddx(1,3) - ddx(3,2) = ddx(2,3) - - END FUNCTION SecondDerivatives3D -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -!> Return the values of the reference element basis functions. In the case of -!> p-element, the values of the lowest-order basis functions corresponding -!> to the background mesh are returned. -!------------------------------------------------------------------------------ - SUBROUTINE NodalBasisFunctions( n, Basis, element, u, v, w) -!------------------------------------------------------------------------------ - INTEGER :: n !< The number of (background) element nodes - REAL(KIND=dp) :: Basis(:) !< The values of reference element basis - TYPE(Element_t) :: element !< The element structure - REAL(KIND=dp) :: u,v,w !< The coordinates of the reference element point -!------------------------------------------------------------------------------ - INTEGER :: i, q, dim - REAL(KIND=dp) :: NodalBasis(n) - - dim = Element % TYPE % DIMENSION - - IF ( isActivePElement(Element) ) THEN - SELECT CASE(dim) - CASE(1) - CALL NodalBasisFunctions1D( Basis, element, u ) - CASE(2) - IF (isPTriangle(Element)) THEN - DO q=1,n - Basis(q) = TriangleNodalPBasis(q, u, v) - END DO - ELSE IF (isPQuad(Element)) THEN - DO q=1,n - Basis(q) = QuadNodalPBasis(q, u, v) - END DO - END IF - CASE(3) - IF (isPTetra( Element )) THEN - DO q=1,n - Basis(q) = TetraNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPWedge( Element )) THEN - DO q=1,n - Basis(q) = WedgeNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPPyramid( Element )) THEN - DO q=1,n - Basis(q) = PyramidNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPBrick( Element )) THEN - DO q=1,n - Basis(q) = BrickNodalPBasis(q, u, v, w) - END DO - END IF - END SELECT - ELSE - SELECT CASE( dim ) - CASE(1) - CALL NodalBasisFunctions1D( Basis, element, u ) - CASE(2) - CALL NodalBasisFunctions2D( Basis, element, u,v ) - CASE(3) - IF ( Element % TYPE % ElementCode/100==6 ) THEN - NodalBasis=0 - DO q=1,n - NodalBasis(q) = 1.0d0 - Basis(q) = InterpolateInElement3D( element, NodalBasis, u,v,w ) - NodalBasis(q) = 0.0d0 - END DO - ELSE - CALL NodalBasisFunctions3D( Basis, element, u,v,w ) - END IF - END SELECT - END IF -!------------------------------------------------------------------------------ - END SUBROUTINE NodalBasisFunctions -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -!> Return the gradient of the reference element basis functions, with the -!> gradient taken with respect to the reference element coordinates. In the case -!> of p-element, the gradients of the lowest-order basis functions corresponding -!> to the background mesh are returned. -!------------------------------------------------------------------------------ - SUBROUTINE NodalFirstDerivatives( n, dLBasisdx, element, u, v, w) -!------------------------------------------------------------------------------ - INTEGER :: n !< The number of (background) element nodes - REAL(KIND=dp) :: dLBasisdx(:,:) !< The gradient of reference element basis functions - TYPE(Element_t) :: element !< The element structure - REAL(KIND=dp) :: u,v,w !< The coordinates of the reference element point -!------------------------------------------------------------------------------ - INTEGER :: i, q, dim - REAL(KIND=dp) :: NodalBasis(n) -!------------------------------------------------------------------------------ - dim = Element % TYPE % DIMENSION - - IF ( IsActivePElement(Element) ) THEN - SELECT CASE(dim) - CASE(1) - CALL NodalFirstDerivatives1D( dLBasisdx, element, u ) - CASE(2) - IF (isPTriangle(Element)) THEN - DO q=1,n - dLBasisdx(q,1:2) = dTriangleNodalPBasis(q, u, v) - END DO - ELSE IF (isPQuad(Element)) THEN - DO q=1,n - dLBasisdx(q,1:2) = dQuadNodalPBasis(q, u, v) - END DO - END IF - CASE(3) - IF (isPTetra( Element )) THEN - DO q=1,n - dLBasisdx(q,1:3) = dTetraNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPWedge( Element )) THEN - DO q=1,n - dLBasisdx(q,1:3) = dWedgeNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPPyramid( Element )) THEN - DO q=1,n - dLBasisdx(q,1:3) = dPyramidNodalPBasis(q, u, v, w) - END DO - ELSE IF (isPBrick( Element )) THEN - DO q=1,n - dLBasisdx(q,1:3) = dBrickNodalPBasis(q, u, v, w) - END DO - END IF - END SELECT - ELSE - SELECT CASE(dim) - CASE(1) - CALL NodalFirstDerivatives1D( dLBasisdx, element, u ) - CASE(2) - CALL NodalFirstDerivatives2D( dLBasisdx, element, u,v ) - CASE(3) - IF ( Element % TYPE % ElementCode / 100 == 6 ) THEN - NodalBasis=0 - DO q=1,n - NodalBasis(q) = 1.0d0 - dLBasisdx(q,1) = FirstDerivativeInU3D(element,NodalBasis,u,v,w) - dLBasisdx(q,2) = FirstDerivativeInV3D(element,NodalBasis,u,v,w) - dLBasisdx(q,3) = FirstDerivativeInW3D(element,NodalBasis,u,v,w) - NodalBasis(q) = 0.0d0 - END DO - ELSE - CALL NodalFirstDerivatives3D( dLBasisdx, element, u,v,w ) - END IF - END SELECT - END IF -!------------------------------------------------------------------------------ - END SUBROUTINE NodalFirstDerivatives -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Return basis function degrees -!------------------------------------------------------------------------------ - SUBROUTINE ElementBasisDegree( Element, BasisDegree ) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - INTEGER :: BasisDegree(:)!< Degree of each basis function in Basis(:) vector. -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: t,s - LOGICAL :: invert, degrees - INTEGER :: i, j, k, l, q, p, f, n, nb, dim, cdim, locali, localj, & - tmp(4), direction(4) - - TYPE(Element_t) :: Bubble - TYPE(Element_t), POINTER :: Edge, Face -!------------------------------------------------------------------------------ - - n = Element % TYPE % NumberOfNodes - dim = Element % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - - BasisDegree = 0 - BasisDegree(1:n) = Element % Type % BasisFunctionDegree - - IF ( isActivePElement(element) ) THEN - - ! Check for need of P basis degrees and set degree of - ! linear basis if vector asked: - ! --------------------------------------------------- - BasisDegree(1:n) = 1 - q = n - -!------------------------------------------------------------------------------ - SELECT CASE( Element % TYPE % ElementCode ) -!------------------------------------------------------------------------------ - - ! P element code for line element: - ! -------------------------------- - CASE(202) - ! Bubbles of line element - IF (Element % BDOFs > 0) THEN - ! For each bubble in line element get value of basis function - DO i=1, Element % BDOFs - IF (q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 1+i - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for edges and bubbles of triangle - CASE(303) - ! Edges of triangle - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge calculate the value of edge basis function - DO i=1,3 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! For each dof in edge get value of p basis function - DO k=1,Edge % BDOFs - IF (q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Bubbles of p triangle - IF ( Element % BDOFs > 0 ) THEN - ! Get element p - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p = CEILING( ( 3.0d0+SQRT(1.0d0+8.0d0*nb) ) / 2.0d0 - AEPS ) - - DO i = 0,p-3 - DO j = 0,p-i-3 - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 3+i+j - END DO - END DO - END IF -!------------------------------------------------------------------------------ -! P element code for quadrilateral edges and bubbles - CASE(404) - ! Edges of p quadrilateral - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge begin node calculate values of edge functions - DO i=1,4 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - ! For each DOF in edge calculate value of p basis function - DO k=1,Edge % BDOFs - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Bubbles of p quadrilateral - IF ( Element % BDOFs > 0 ) THEN - ! Get element P - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p = CEILING( ( 5.0d0+SQRT(1.0d0+8.0d0*nb) ) / 2.0d0 - AEPS) - - DO i=2,(p-2) - DO j=2,(p-i) - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = i+j - END DO - END DO - END IF -!------------------------------------------------------------------------------ -! P element code for tetrahedron edges, faces and bubbles - CASE(504) - ! Edges of p tetrahedron - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge calculate value of edge functions - DO i=1,6 - Edge => CurrentModel % Solver % Mesh % Edges (Element % EdgeIndexes(i)) - - ! Do not solve edge DOFS if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! For each DOF in edge calculate value of edge functions - ! and their derivatives for edge=i, i=k+1 - DO k=1, Edge % BDOFs - IF (q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of p tetrahedron - IF ( ASSOCIATED( Element % FaceIndexes )) THEN - ! For each face calculate value of face functions - DO F=1,4 - Face => CurrentModel % Solver % Mesh % Faces (Element % FaceIndexes(F)) - - ! Do not solve face DOFs if there is not any - IF (Face % BDOFs <= 0) CYCLE - - ! Get face p - p = Face % PDefs % P - - ! For each DOF in face calculate value of face functions and - ! their derivatives for face=F and index pairs - ! i,j=0,..,p-3, i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF (q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 3+i+j - END DO - END DO - END DO - END IF - - ! Bubbles of p tetrahedron - IF ( Element % BDOFs > 0 ) THEN - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+2 - AEPS) - - DO i=0,p-4 - DO j=0,p-i-4 - DO k=0,p-i-j-4 - IF (q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 4+i+j+k - END DO - END DO - END DO - - END IF -!------------------------------------------------------------------------------ -! P element code for pyramid edges, faces and bubbles - CASE(605) - ! Edges of P Pyramid - IF (ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in wedge, calculate values of edge functions - DO i=1,8 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P Pyramid - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in pyramid, calculate values of face functions - DO F=1,5 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not solve face dofs, if there is not any - IF ( Face % BDOFs <= 0) CYCLE - - ! Get face p - p = Face % PDefs % P - - ! Handle triangle and square faces separately - SELECT CASE(F) - CASE (1) - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = i+j - END DO - END DO - - CASE (2,3,4,5) - ! For each face calculate values of functions from index - ! pairs i,j=0,..,p-3 i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 3+i+j - END DO - END DO - END SELECT - END DO - END IF - - ! Bubbles of P Pyramid - IF (Element % BDOFs > 0) THEN - ! Get element p - p = Element % PDefs % p - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+2 - AEPS) - - ! Calculate value of bubble functions from indexes - ! i,j,k=0,..,p-4 i+j+k=0,..,p-4 - DO i=0,p-4 - DO j=0,p-i-4 - DO k=0,p-i-j-4 - IF ( q >= SIZE(BasisDegree)) CYCLE - q = q + 1 - BasisDegree(q) = 4+i+j+k - END DO - END DO - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for wedge edges, faces and bubbles - CASE(706) - ! Edges of P Wedge - IF (ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in wedge, calculate values of edge functions - DO i=1,9 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - - ! Use basis compatible with pyramid if necessary - ! @todo Correct this! - IF (Edge % PDefs % pyramidQuadEdge) THEN - CALL Fatal('ElementInfo','Pyramid compatible wedge edge basis NIY!') - END IF - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P Wedge - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in wedge, calculate values of face functions - DO F=1,5 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not solve face dofs, if there is not any - IF ( Face % BDOFs <= 0) CYCLE - - p = Face % PDefs % P - - ! Handle triangle and square faces separately - SELECT CASE(F) - CASE (1,2) - ! For each face calculate values of functions from index - ! pairs i,j=0,..,p-3 i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 3+i+j - END DO - END DO - CASE (3,4,5) - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = i+j - END DO - END DO - END SELECT - - END DO - END IF - - ! Bubbles of P Wedge - IF ( Element % BDOFs > 0 ) THEN - ! Get p from element - p = Element % PDefs % P - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+3 - AEPS) - - ! For each bubble calculate value of basis function and its derivative - ! for index pairs i,j=0,..,p-5 k=2,..,p-3 i+j+k=2,..,p-3 - DO i=0,p-5 - DO j=0,p-5-i - DO k=2,p-3-i-j - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 3+i+j+k - END DO - END DO - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for brick edges, faces and bubbles - CASE(808) - ! Edges of P brick - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in brick, calculate values of edge functions - DO i=1,12 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P brick - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in brick, calculate values of face functions - DO F=1,6 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not calculate face values if no dofs - IF (Face % BDOFs <= 0) CYCLE - - ! Get p for face - p = Face % PDefs % P - - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = i+j - END DO - END DO - END DO - END IF - - ! Bubbles of p brick - IF ( Element % BDOFs > 0 ) THEN - ! Get p from bubble DOFs - p = Element % PDefs % P - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+4 - AEPS) - - ! For each bubble calculate value of basis function and its derivative - ! for index pairs i,j,k=2,..,p-4, i+j+k=6,..,p - DO i=2,p-4 - DO j=2,p-i-2 - DO k=2,p-i-j - IF ( q >= SIZE(BasisDegree) ) CYCLE - q = q + 1 - BasisDegree(q) = i+j+k - END DO - END DO - END DO - END IF - - END SELECT - END IF ! P element flag check -!------------------------------------------------------------------------------ - END SUBROUTINE ElementBasisDegree -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Return the referencial description b(f(p)) of the basis function b(x), -!> with f mapping points p on a reference element to points x on a physical -!> element. The referencial description of the spatial gradient field grad b -!> and, if requested, the second spatial derivatives may also be returned. -!> Also return the square root of the determinant of the metric tensor -!> (=sqrt(det(J^TJ))) related to the mapping f. -!------------------------------------------------------------------------------ - RECURSIVE FUNCTION ElementInfo( Element, Nodes, u, v, w, detJ, & - Basis, dBasisdx, ddBasisddx, SecondDerivatives, Bubbles, BasisDegree, & - EdgeBasis, RotBasis, USolver ) RESULT(stat) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Element nodal coordinates. - REAL(KIND=dp) :: u !< 1st local coordinate at which to calculate the basis function. - REAL(KIND=dp) :: v !< 2nd local coordinate. - REAL(KIND=dp) :: w !< 3rd local coordinate. - REAL(KIND=dp) :: detJ !< Square root of determinant of element coordinate system metric - REAL(KIND=dp) :: Basis(:) !< Basis function values at p=(u,v,w) - REAL(KIND=dp), OPTIONAL :: dBasisdx(:,:) !< Global first derivatives of basis functions at (u,v,w) - REAL(KIND=dp), OPTIONAL :: ddBasisddx(:,:,:) !< Global second derivatives of basis functions at (u,v,w) if requested - INTEGER, OPTIONAL :: BasisDegree(:) !< Degree of each basis function in Basis(:) vector. - !! May be used with P element basis functions - LOGICAL, OPTIONAL :: SecondDerivatives !< Are the second derivatives needed? (still present for historical reasons) - TYPE(Solver_t), POINTER, OPTIONAL :: USolver !< The solver used to call the basis functions. - LOGICAL, OPTIONAL :: Bubbles !< Are the bubbles to be evaluated. - REAL(KIND=dp), OPTIONAL :: EdgeBasis(:,:) !< If present, the values of H(curl)-conforming basis functions B(f(p)) - REAL(KIND=dp), OPTIONAL :: RotBasis(:,:) !< The referencial description of the spatial curl of B - LOGICAL :: Stat !< If .FALSE. element is degenerate. -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - TYPE(Solver_t), POINTER :: PSolver => NULL() - REAL(KIND=dp) :: BubbleValue, dBubbledx(3), t, s, LtoGMap(3,3) - LOGICAL :: invert, degrees - INTEGER :: i, j, k, l, q, p, f, n, nb, dim, cdim, locali, localj, & - tmp(4), direction(4) - REAL(KIND=dp) :: LinBasis(8), dLinBasisdx(8,3), ElmMetric(3,3) - - REAL(KIND=dp) :: NodalBasis(Element % TYPE % NumberOfNodes), & - dLBasisdx(MAX(SIZE(Nodes % x),SIZE(Basis)),3) - - TYPE(Element_t) :: Bubble - TYPE(Element_t), POINTER :: Edge, Face - INTEGER :: EdgeBasisDegree - LOGICAL :: PerformPiolaTransform, Found - - SAVE PSolver, EdgeBasisDegree, PerformPiolaTransform -!------------------------------------------------------------------------------ - IF(PRESENT(EdgeBasis)) THEN - IF( PRESENT( USolver ) ) THEN - IF( .NOT. ASSOCIATED( USolver, PSolver ) ) THEN - IF( ListGetLogical(USolver % Values,'Quadratic Approximation', Found ) ) THEN - EdgeBasisDegree = 2 - PerformPiolaTransform = .TRUE. - ELSE - EdgeBasisDegree = 1 - PerformPiolaTransform = ListGetLogical(USolver % Values,'Use Piola Transform', Found ) - END IF - PSolver => USolver - END IF - ELSE - EdgeBasisDegree = 1 - PerformPiolaTransform = .TRUE. - END IF - IF( PerformPiolaTransform ) THEN - stat = EdgeElementInfo(Element,Nodes,u,v,w,detF=Detj,Basis=Basis, & - EdgeBasis=EdgeBasis,RotBasis=RotBasis,dBasisdx=dBasisdx,& - BasisDegree = EdgeBasisDegree, ApplyPiolaTransform = PerformPiolaTransform ) - ELSE - ! Is this really necessary to call in case no piola version? - stat = ElementInfo( Element, Nodes, u, v, w, detJ, Basis, dBasisdx ) - CALL GetEdgeBasis(Element,EdgeBasis,RotBasis,Basis,dBasisdx) - END IF - RETURN - END IF - - stat = .TRUE. - n = Element % TYPE % NumberOfNodes - dim = Element % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - - IF ( Element % TYPE % ElementCode == 101 ) THEN - detJ = 1.0d0 - Basis(1) = 1.0d0 - IF ( PRESENT(dBasisdx) ) dBasisdx(1,:) = 0.0d0 - RETURN - END IF - - Basis = 0.0d0 - CALL NodalBasisFunctions(n, Basis, element, u, v, w) - - dLbasisdx = 0.0d0 - CALL NodalFirstDerivatives(n, dLBasisdx, element, u, v, w) - - q = n - - ! P ELEMENT CODE: - ! --------------- - IF ( isActivePElement(element) ) THEN - - ! Check for need of P basis degrees and set degree of - ! linear basis if vector asked: - ! --------------------------------------------------- - degrees = .FALSE. - IF ( PRESENT(BasisDegree)) THEN - degrees = .TRUE. - BasisDegree = 0 - BasisDegree(1:n) = 1 - END IF - -!------------------------------------------------------------------------------ - SELECT CASE( Element % TYPE % ElementCode ) -!------------------------------------------------------------------------------ - - ! P element code for line element: - ! -------------------------------- - CASE(202) - ! Bubbles of line element - IF (Element % BDOFs > 0) THEN - ! For boundary element integration check direction - invert = .FALSE. - IF ( Element % PDefs % isEdge .AND. & - Element % NodeIndexes(1)>Element % NodeIndexes(2) ) invert = .TRUE. - - ! For each bubble in line element get value of basis function - DO i=1, Element % BDOFs - IF (q >= SIZE(Basis)) CYCLE - q = q + 1 - - Basis(q) = LineBubblePBasis(i+1,u,invert) - dLBasisdx(q,1) = dLineBubblePBasis(i+1,u,invert) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+i - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for edges and bubbles of triangle - CASE(303) - ! Edges of triangle - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge calculate the value of edge basis function - DO i=1,3 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Get local number of edge start and endpoint nodes - tmp(1:2) = getTriangleEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Invert edge for parity if needed - invert = .FALSE. - IF ( Element % NodeIndexes(locali)>Element % NodeIndexes(localj) ) invert=.TRUE. - - ! For each dof in edge get value of p basis function - DO k=1,Edge % BDOFs - IF (q >= SIZE(Basis)) CYCLE - q = q + 1 - - ! Value of basis functions for edge=i and i=k+1 by parity - Basis(q) = TriangleEdgePBasis(i, k+1, u, v, invert) - ! Value of derivative of basis function - dLBasisdx(q,1:2) = dTriangleEdgePBasis(i, k+1, u, v, invert) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Bubbles of p triangle - IF ( Element % BDOFs > 0 ) THEN - ! Get element p - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p = CEILING( ( 3.0d0+SQRT(1.0d0+8.0d0*nb) ) / 2.0d0 - AEPS) - - ! For boundary element direction needs to be calculated - IF (Element % PDefs % isEdge) THEN - direction = 0 - ! Get direction of this face (mask for face = boundary element nodes) - direction(1:3) = getTriangleFaceDirection(Element, [ 1,2,3 ]) - END IF - - DO i = 0,p-3 - DO j = 0,p-i-3 - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! Get bubble basis functions and their derivatives - ! 3d Boundary element has a direction - IF (Element % PDefs % isEdge) THEN - Basis(q) = TriangleEBubblePBasis(i,j,u,v,direction) - dLBasisdx(q,1:2) = dTriangleEBubblePBasis(i,j,u,v,direction) - ELSE - ! 2d element bubbles have no direction - Basis(q) = TriangleBubblePBasis(i,j,u,v) - dLBasisdx(q,1:2) = dTriangleBubblePBasis(i,j,u,v) - END IF - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 3+i+j - END DO - END DO - END IF -!------------------------------------------------------------------------------ -! P element code for quadrilateral edges and bubbles - CASE(404) - ! Edges of p quadrilateral - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge begin node calculate values of edge functions - DO i=1,4 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Choose correct parity by global edge dofs - tmp(1:2) = getQuadEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Invert parity if needed - invert = .FALSE. - IF (Element % NodeIndexes(locali) > Element % NodeIndexes(localj)) invert = .TRUE. - - ! For each DOF in edge calculate value of p basis function - DO k=1,Edge % BDOFs - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! For pyramid square face edges use different basis - IF (Edge % PDefs % pyramidQuadEdge) THEN - Basis(q) = QuadPyraEdgePBasis(i,k+1,u,v,invert) - dLBasisdx(q,1:2) = dQuadPyraEdgePBasis(i,k+1,u,v,invert) - ! Normal case, use basis of quadrilateral - ELSE - ! Get values of basis functions for edge=i and i=k+1 by parity - Basis(q) = QuadEdgePBasis(i,k+1,u,v,invert) - ! Get value of derivatives of basis functions - dLBasisdx(q,1:2) = dQuadEdgePBasis(i,k+1,u,v,invert) - END IF - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Bubbles of p quadrilateral - IF ( Element % BDOFs > 0 ) THEN - ! Get element P - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p = CEILING( ( 5.0d0+SQRT(1.0d0+8.0d0*nb) ) / 2.0d0 - AEPS) - - ! For boundary element direction needs to be calculated - IF (Element % PDefs % isEdge) THEN - direction = 0 - direction = getSquareFaceDirection(Element, [ 1,2,3,4 ]) - END IF - - ! For each bubble calculate value of p basis function - ! and their derivatives for index pairs i,j>=2, i+j=4,...,p - DO i=2,(p-2) - DO j=2,(p-i) - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! Get values of bubble functions - ! 3D boundary elements have a direction - IF (Element % PDefs % isEdge) THEN - Basis(q) = QuadBubblePBasis(i,j,u,v,direction) - dLBasisdx(q,1:2) = dQuadBubblePBasis(i,j,u,v,direction) - ELSE - ! 2d element bubbles have no direction - Basis(q) = QuadBubblePBasis(i,j,u,v) - dLBasisdx(q,1:2) = dQuadBubblePBasis(i,j,u,v) - END IF - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = i+j - END DO - END DO - END IF -!------------------------------------------------------------------------------ -! P element code for tetrahedron edges, faces and bubbles - CASE(504) - ! Edges of p tetrahedron - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge calculate value of edge functions - DO i=1,6 - Edge => CurrentModel % Solver % Mesh % Edges (Element % EdgeIndexes(i)) - - ! Do not solve edge DOFS if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! For each DOF in edge calculate value of edge functions - ! and their derivatives for edge=i, i=k+1 - DO k=1, Edge % BDOFs - IF (q >= SIZE(Basis)) CYCLE - q = q + 1 - - Basis(q) = TetraEdgePBasis(i,k+1,u,v,w, Element % PDefs % TetraType) - dLBasisdx(q,1:3) = dTetraEdgePBasis(i,k+1,u,v,w, Element % PDefs % TetraType) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of p tetrahedron - IF ( ASSOCIATED( Element % FaceIndexes )) THEN - ! For each face calculate value of face functions - DO F=1,4 - Face => CurrentModel % Solver % Mesh % Faces (Element % FaceIndexes(F)) - - ! Do not solve face DOFs if there is not any - IF (Face % BDOFs <= 0) CYCLE - - ! Get face p - p = Face % PDefs % P - - ! For each DOF in face calculate value of face functions and - ! their derivatives for face=F and index pairs - ! i,j=0,..,p-3, i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF (q >= SIZE(Basis)) CYCLE - q = q + 1 - - Basis(q) = TetraFacePBasis(F,i,j,u,v,w, Element % PDefs % TetraType) - dLBasisdx(q,1:3) = dTetraFacePBasis(F,i,j,u,v,w, Element % PDefs % TetraType) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 3+i+j - END DO - END DO - END DO - END IF - - ! Bubbles of p tetrahedron - IF ( Element % BDOFs > 0 ) THEN - p = Element % PDefs % P - - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+2 - AEPS) - - ! For each DOF in bubbles calculate value of bubble functions - ! and their derivatives for index pairs - ! i,j,k=0,..,p-4 i+j+k=0,..,p-4 - DO i=0,p-4 - DO j=0,p-i-4 - DO k=0,p-i-j-4 - IF (q >= SIZE(Basis)) CYCLE - q = q + 1 - - Basis(q) = TetraBubblePBasis(i,j,k,u,v,w) - dLBasisdx(q,1:3) = dTetraBubblePBasis(i,j,k,u,v,w) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 4+i+j+k - END DO - END DO - END DO - - END IF -!------------------------------------------------------------------------------ -! P element code for pyramid edges, faces and bubbles - CASE(605) - ! Edges of P Pyramid - IF (ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in wedge, calculate values of edge functions - DO i=1,8 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! Get local indexes of current edge - tmp(1:2) = getPyramidEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Determine edge direction - invert = .FALSE. - - ! Invert edge if local first node has greater global index than second one - IF ( Element % NodeIndexes(locali) > Element % NodeIndexes(localj) ) invert = .TRUE. - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! Get values of edge basis functions and their derivatives - Basis(q) = PyramidEdgePBasis(i,k+1,u,v,w,invert) - dLBasisdx(q,1:3) = dPyramidEdgePBasis(i,k+1,u,v,w,invert) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P Pyramid - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in pyramid, calculate values of face functions - DO F=1,5 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not solve face dofs, if there is not any - IF ( Face % BDOFs <= 0) CYCLE - - ! Get face p - p = Face % PDefs % P - - ! Handle triangle and square faces separately - SELECT CASE(F) - CASE (1) - direction = 0 - ! Get global direction vector for enforcing parity - tmp(1:4) = getPyramidFaceMap(F) - direction(1:4) = getSquareFaceDirection( Element, tmp(1:4) ) - - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - Basis(q) = PyramidFacePBasis(F,i,j,u,v,w,direction) - dLBasisdx(q,:) = dPyramidFacePBasis(F,i,j,u,v,w,direction) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = i+j - END DO - END DO - - CASE (2,3,4,5) - direction = 0 - ! Get global direction vector for enforcing parity - tmp(1:4) = getPyramidFaceMap(F) - direction(1:3) = getTriangleFaceDirection( Element, tmp(1:3) ) - - ! For each face calculate values of functions from index - ! pairs i,j=0,..,p-3 i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - Basis(q) = PyramidFacePBasis(F,i,j,u,v,w,direction) - dLBasisdx(q,:) = dPyramidFacePBasis(F,i,j,u,v,w,direction) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 3+i+j - END DO - END DO - END SELECT - END DO - END IF - - ! Bubbles of P Pyramid - IF (Element % BDOFs > 0) THEN - ! Get element p - p = Element % PDefs % p - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+2 - AEPS) - - ! Calculate value of bubble functions from indexes - ! i,j,k=0,..,p-4 i+j+k=0,..,p-4 - DO i=0,p-4 - DO j=0,p-i-4 - DO k=0,p-i-j-4 - IF ( q >= SIZE(Basis)) CYCLE - q = q + 1 - - Basis(q) = PyramidBubblePBasis(i,j,k,u,v,w) - dLBasisdx(q,:) = dPyramidBubblePBasis(i,j,k,u,v,w) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 4+i+j+k - END DO - END DO - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for wedge edges, faces and bubbles - CASE(706) - ! Edges of P Wedge - IF (ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in wedge, calculate values of edge functions - DO i=1,9 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! Get local indexes of current edge - tmp(1:2) = getWedgeEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Determine edge direction - invert = .FALSE. - ! Invert edge if local first node has greater global index than second one - IF ( Element % NodeIndexes(locali) > Element % NodeIndexes(localj) ) invert = .TRUE. - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! Use basis compatible with pyramid if necessary - ! @todo Correct this! - IF (Edge % PDefs % pyramidQuadEdge) THEN - CALL Fatal('ElementInfo','Pyramid compatible wedge edge basis NIY!') - END IF - - ! Get values of edge basis functions and their derivatives - Basis(q) = WedgeEdgePBasis(i,k+1,u,v,w,invert) - dLBasisdx(q,1:3) = dWedgeEdgePBasis(i,k+1,u,v,w,invert) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P Wedge - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in wedge, calculate values of face functions - DO F=1,5 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not solve face dofs, if there is not any - IF ( Face % BDOFs <= 0) CYCLE - - p = Face % PDefs % P - - ! Handle triangle and square faces separately - SELECT CASE(F) - CASE (1,2) - direction = 0 - ! Get global direction vector for enforcing parity - tmp(1:4) = getWedgeFaceMap(F) - direction(1:3) = getTriangleFaceDirection( Element, tmp(1:3) ) - - ! For each face calculate values of functions from index - ! pairs i,j=0,..,p-3 i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - Basis(q) = WedgeFacePBasis(F,i,j,u,v,w,direction) - dLBasisdx(q,:) = dWedgeFacePBasis(F,i,j,u,v,w,direction) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 3+i+j - END DO - END DO - CASE (3,4,5) - direction = 0 - ! Get global direction vector for enforcing parity - invert = .FALSE. - tmp(1:4) = getWedgeFaceMap(F) - direction(1:4) = getSquareFaceDirection( Element, tmp(1:4) ) - - ! First and second node must form a face in upper or lower triangle - IF (.NOT. wedgeOrdering(direction)) THEN - invert = .TRUE. - tmp(1) = direction(2) - direction(2) = direction(4) - direction(4) = tmp(1) - END IF - - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - IF (.NOT. invert) THEN - Basis(q) = WedgeFacePBasis(F,i,j,u,v,w,direction) - dLBasisdx(q,:) = dWedgeFacePBasis(F,i,j,u,v,w,direction) - ELSE - Basis(q) = WedgeFacePBasis(F,j,i,u,v,w,direction) - dLBasisdx(q,:) = dWedgeFacePBasis(F,j,i,u,v,w,direction) - END IF - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = i+j - END DO - END DO - END SELECT - - END DO - END IF - - ! Bubbles of P Wedge - IF ( Element % BDOFs > 0 ) THEN - ! Get p from element - p = Element % PDefs % P - nb = MAX( GetBubbleDOFs( Element, p ), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+3 - AEPS) - - ! For each bubble calculate value of basis function and its derivative - ! for index pairs i,j=0,..,p-5 k=2,..,p-3 i+j+k=2,..,p-3 - DO i=0,p-5 - DO j=0,p-5-i - DO k=2,p-3-i-j - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - Basis(q) = WedgeBubblePBasis(i,j,k,u,v,w) - dLBasisdx(q,:) = dWedgeBubblePBasis(i,j,k,u,v,w) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 3+i+j+k - END DO - END DO - END DO - END IF - -!------------------------------------------------------------------------------ -! P element code for brick edges, faces and bubbles - CASE(808) - ! Edges of P brick - IF ( ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in brick, calculate values of edge functions - DO i=1,12 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! Get local indexes of current edge - tmp(1:2) = getBrickEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Determine edge direction - invert = .FALSE. - - ! Invert edge if local first node has greater global index than second one - IF ( Element % NodeIndexes(locali) > Element % NodeIndexes(localj) ) invert = .TRUE. - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - - ! For edges connected to pyramid square face, use different basis - IF (Edge % PDefs % pyramidQuadEdge) THEN - ! Get values of edge basis functions and their derivatives - Basis(q) = BrickPyraEdgePBasis(i,k+1,u,v,w,invert) - dLBasisdx(q,1:3) = dBrickPyraEdgePBasis(i,k+1,u,v,w,invert) - ! Normal case. Use standard brick edge functions - ELSE - ! Get values of edge basis functions and their derivatives - Basis(q) = BrickEdgePBasis(i,k+1,u,v,w,invert) - dLBasisdx(q,1:3) = dBrickEdgePBasis(i,k+1,u,v,w,invert) - END IF - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = 1+k - END DO - END DO - END IF - - ! Faces of P brick - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in brick, calculate values of face functions - DO F=1,6 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not calculate face values if no dofs - IF (Face % BDOFs <= 0) CYCLE - - ! Get p for face - p = Face % PDefs % P - - ! Generate direction vector for this face - tmp(1:4) = getBrickFaceMap(F) - direction(1:4) = getSquareFaceDirection(Element, tmp) - - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - Basis(q) = BrickFacePBasis(F,i,j,u,v,w,direction) - dLBasisdx(q,:) = dBrickFacePBasis(F,i,j,u,v,w,direction) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = i+j - END DO - END DO - END DO - END IF - - ! Bubbles of p brick - IF ( Element % BDOFs > 0 ) THEN - ! Get p from bubble DOFs - p = Element % PDefs % P - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+4 - AEPS) - - - ! For each bubble calculate value of basis function and its derivative - ! for index pairs i,j,k=2,..,p-4, i+j+k=6,..,p - DO i=2,p-4 - DO j=2,p-i-2 - DO k=2,p-i-j - IF ( q >= SIZE(Basis) ) CYCLE - q = q + 1 - Basis(q) = BrickBubblePBasis(i,j,k,u,v,w) - dLBasisdx(q,:) = dBrickBubblePBasis(i,j,k,u,v,w) - - ! Polynomial degree of basis function to vector - IF (degrees) BasisDegree(q) = i+j+k - END DO - END DO - END DO - END IF - - END SELECT - END IF ! P element flag check -!------------------------------------------------------------------------------ - - ! Element (contravariant) metric and square root of determinant - !-------------------------------------------------------------- - IF ( .NOT. ElementMetric( q, Element, Nodes, & - ElmMetric, detJ, dLBasisdx, LtoGMap ) ) THEN - stat = .FALSE. - RETURN - END IF - - ! Get global first derivatives: - !------------------------------ - IF ( PRESENT(dBasisdx) ) THEN - dBasisdx = 0.0d0 - DO i=1,q - DO j=1,cdim - DO k=1,dim - dBasisdx(i,j) = dBasisdx(i,j) + dLBasisdx(i,k)*LtoGMap(j,k) - END DO - END DO - END DO - END IF - - ! Get matrix of second derivatives, if needed: - !--------------------------------------------- - IF ( PRESENT(ddBasisddx) .AND. PRESENT(SecondDerivatives) ) THEN - IF ( SecondDerivatives ) THEN - NodalBasis = 0.0d0 - ddBasisddx(1:n,:,:) = 0.0d0 - DO q=1,n - NodalBasis(q) = 1.0d0 - CALL GlobalSecondDerivatives(Element,Nodes,NodalBasis, & - ddBasisddx(q,:,:),u,v,w,ElmMetric,dLBasisdx ) - NodalBasis(q) = 0.0d0 - END DO - END IF - END IF - -!------------------------------------------------------------------------------ -! Generate bubble basis functions, if requested. Bubble basis is as follows: -! B_i (=(N_(i+n)) = B * N_i, where N_i:s are the nodal basis functions of -! the element, and B the basic bubble, i.e. the product of nodal basis -! functions of the corresponding linear element for triangles and tetras, -! and product of two diagonally opposed nodal basisfunctions of the -! corresponding (bi-,tri-)linear element for 1d-elements, quads and hexas. -!------------------------------------------------------------------------------ - IF ( PRESENT( Bubbles ) ) THEN - Bubble % BDOFs = 0 - NULLIFY( Bubble % PDefs ) - NULLIFY( Bubble % EdgeIndexes ) - NULLIFY( Bubble % FaceIndexes ) - NULLIFY( Bubble % BubbleIndexes ) - - IF ( Bubbles .AND. SIZE(Basis) >= 2*n ) THEN - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(2) - - IF ( Element % TYPE % ElementCode == 202 ) THEN - LinBasis(1:n) = Basis(1:n) - dLinBasisdx(1:n,1:cdim) = dBasisdx(1:n,1:cdim) - ELSE - Bubble % TYPE => GetElementType(202) - - stat = ElementInfo( Bubble, nodes, u, v, w, detJ, & - LinBasis, dLinBasisdx ) - END IF - - BubbleValue = LinBasis(1) * LinBasis(2) - - DO i=1,n - Basis(n+i) = Basis(i) * BubbleValue - DO j=1,cdim - dBasisdx(n+i,j) = dBasisdx(i,j) * BubbleValue - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(1,j) * LinBasis(2) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(2,j) * LinBasis(1) - END DO - END DO - - CASE(3) - - IF ( Element % TYPE % ElementCode == 303 ) THEN - LinBasis(1:n) = Basis(1:n) - dLinBasisdx(1:n,1:cdim) = dBasisdx(1:n,1:cdim) - ELSE - Bubble % TYPE => GetElementType(303) - - stat = ElementInfo( Bubble, nodes, u, v, w, detJ, & - LinBasis, dLinBasisdx ) - END IF - - BubbleValue = LinBasis(1) * LinBasis(2) * LinBasis(3) - - DO i=1,n - Basis(n+i) = Basis(i) * BubbleValue - DO j=1,cdim - dBasisdx(n+i,j) = dBasisdx(i,j) * BubbleValue - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(1,j) * LinBasis(2) * LinBasis(3) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(2,j) * LinBasis(1) * LinBasis(3) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(3,j) * LinBasis(1) * LinBasis(2) - END DO - END DO - - CASE(4) - - IF ( Element % TYPE % ElementCode == 404 ) THEN - LinBasis(1:n) = Basis(1:n) - dLinBasisdx(1:n,1:cdim) = dBasisdx(1:n,1:cdim) - ELSE - Bubble % TYPE => GetElementType(404) - - stat = ElementInfo( Bubble, nodes, u, v, w, detJ, & - LinBasis, dLinBasisdx ) - END IF - - BubbleValue = LinBasis(1) * LinBasis(3) - - DO i=1,n - Basis(n+i) = Basis(i) * BubbleValue - DO j=1,cdim - dBasisdx(n+i,j) = dBasisdx(i,j) * BubbleValue - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(1,j) * LinBasis(3) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(3,j) * LinBasis(1) - END DO - END DO - - CASE(5) - - IF ( Element % TYPE % ElementCode == 504 ) THEN - LinBasis(1:n) = Basis(1:n) - dLinBasisdx(1:n,1:cdim) = dBasisdx(1:n,1:cdim) - ELSE - Bubble % TYPE => GetElementType(504) - - stat = ElementInfo( Bubble, nodes, u, v, w, detJ, & - LinBasis, dLinBasisdx ) - END IF - - BubbleValue = LinBasis(1) * LinBasis(2) * LinBasis(3) * LinBasis(4) - DO i=1,n - Basis(n+i) = Basis(i) * BubbleValue - DO j=1,cdim - dBasisdx(n+i,j) = dBasisdx(i,j) * BubbleValue - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * dLinBasisdx(1,j) * & - LinBasis(2) * LinBasis(3) * LinBasis(4) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * dLinBasisdx(2,j) * & - LinBasis(1) * LinBasis(3) * LinBasis(4) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * dLinBasisdx(3,j) * & - LinBasis(1) * LinBasis(2) * LinBasis(4) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * dLinBasisdx(4,j) * & - LinBasis(1) * LinBasis(2) * LinBasis(3) - END DO - END DO - - CASE(8) - - IF ( Element % TYPE % ElementCode == 808 ) THEN - LinBasis(1:n) = Basis(1:n) - dLinBasisdx(1:n,1:cdim) = dBasisdx(1:n,1:cdim) - ELSE - Bubble % TYPE => GetElementType(808) - - stat = ElementInfo( Bubble, nodes, u, v, w, detJ, & - LinBasis, dLinBasisdx ) - END IF - - BubbleValue = LinBasis(1) * LinBasis(7) - - DO i=1,n - Basis(n+i) = Basis(i) * BubbleValue - DO j=1,cdim - dBasisdx(n+i,j) = dBasisdx(i,j) * BubbleValue - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(1,j) * LinBasis(7) - - dBasisdx(n+i,j) = dBasisdx(n+i,j) + Basis(i) * & - dLinBasisdx(7,j) * LinBasis(1) - END DO - END DO - - CASE DEFAULT - - WRITE( Message, '(a,i4,a)' ) 'Bubbles for element: ', & - Element % TYPE % ElementCode, ' are not implemented.' - CALL Error( 'ElementInfo', Message ) - CALL Fatal( 'ElementInfo', 'Please use p-element basis instead.' ) - - END SELECT - END IF - END IF -!------------------------------------------------------------------------------ - END FUNCTION ElementInfo -!------------------------------------------------------------------------------ - - ! SUBROUTINE ElementInfoVec_InitWork(m, n) - ! IMPLICIT NONE - - ! INTEGER, INTENT(IN) :: m, n - ! INTEGER :: allocstat - - ! allocstat = 0 - ! IF (.NOT. ALLOCATED(BasisWrk)) THEN - ! ALLOCATE(BasisWrk(m,n), & - ! dBasisdxWrk(m,n,3), & - ! LtoGMapsWrk(m,3,3), & - ! DetJWrk(m), & - ! uWrk(m), vWrk(m), wWrk(m), STAT=allocstat) - ! ELSE IF (SIZE(BasisWrk,1) /= m .OR. SIZE(BasisWrk,2) /= n) THEN - ! DEALLOCATE(BasisWrk, dBasisdxWrk, LtoGMapsWrk, DetJWrk, uWrk, vWrk, wWrk) - ! ALLOCATE(BasisWrk(m,n), & - ! dBasisdxWrk(m,n,3), & - ! LtoGMapsWrk(m,3,3), & - ! DetJWrk(m), & - ! uWrk(m), vWrk(m), wWrk(m), STAT=allocstat) - ! END IF - - ! ! Check memory allocation status - ! IF (allocstat /= 0) THEN - ! CALL Error('ElementInfo_InitWork','Storage allocation for local element basis failed') - ! END IF - ! END SUBROUTINE ElementInfoVec_InitWork - - ! SUBROUTINE ElementInfoVec_FreeWork() - ! IMPLICIT NONE - - ! IF (ALLOCATED(BasisWrk)) THEN - ! DEALLOCATE(BasisWrk, dBasisdxWrk, LtoGMapsWrk, DetJWrk, uWrk, vWrk, wWrk) - ! END IF - ! END SUBROUTINE ElementInfoVec_FreeWork - -! ElementInfoVec currently uses only P element definitions for basis -! functions, even for purely nodal elements. Support for standard nodal elements -! will be implemented in the future. -!------------------------------------------------------------------------------ - FUNCTION ElementInfoVec( Element, Nodes, nc, u, v, w, detJ, nbmax, Basis, dBasisdx ) RESULT(retval) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Element nodal coordinates. - INTEGER, INTENT(IN) :: nc !< Number of local coordinates to compute values of the basis function - REAL(KIND=dp), POINTER CONTIG :: u(:) !< 1st local coordinates at which to calculate the basis function. - REAL(KIND=dp), POINTER CONTIG :: v(:) !< 2nd local coordinates. - REAL(KIND=dp), POINTER CONTIG :: w(:) !< 3rd local coordinates. - REAL(KIND=dp) CONTIG, INTENT(OUT) :: detJ(:) !< Square roots of determinants of element coordinate system metric at coordinates - INTEGER, INTENT(IN) :: nbmax !< Maximum number of basis functions to compute - REAL(KIND=dp) CONTIG :: Basis(:,:) !< Basis function values at (u,v,w) - REAL(KIND=dp) CONTIG, OPTIONAL :: dBasisdx(:,:,:) !< Global first derivatives of basis functions at (u,v,w) - LOGICAL :: retval !< If .FALSE. element is degenerate. or if local storage allocation fails - - ! Internal work arrays (always needed) - REAL(KIND=dp) :: uWrk(VECTOR_BLOCK_LENGTH), vWrk(VECTOR_BLOCK_LENGTH), wWrk(VECTOR_BLOCK_LENGTH) - REAL(KIND=dp) :: BasisWrk(VECTOR_BLOCK_LENGTH,nbmax) - REAL(KIND=dp) :: dBasisdxWrk(VECTOR_BLOCK_LENGTH,nbmax,3) - REAL(KIND=dp) :: DetJWrk(VECTOR_BLOCK_LENGTH) - REAL(KIND=dp) :: LtoGMapsWrk(VECTOR_BLOCK_LENGTH,3,3) - - INTEGER :: i -!DIR$ ATTRIBUTES ALIGN:64::uWrk, vWrk, wWrk, BasisWrk, dBasisdxWrk, DetJWrk, LtoGMapsWrk - - !------------------------------------------------------------------------------ - ! Special case, Element: POINT - IF (Element % TYPE % ElementCODE == 101) THEN - DetJ(1:nc) = REAL(1, dp) - Basis(1:nc,1) = REAL(1, dp) - IF (PRESENT(dBasisdx)) THEN - DO i=1,nc - dBasisdx(i,1,1) = REAL(0, dp) - END DO - END IF - retval = .TRUE. - RETURN - END IF - - ! Set up workspace arrays - ! CALL ElementInfoVec_InitWork(VECTOR_BLOCK_LENGTH, nbmax) - IF ( nbmax < Element % TYPE % NumberOfNodes ) THEN - CALL Fatal('ElementInfoVec','Not enough storage to compute local element basis') - END IF - - IF(PRESENT(dBasisdx)) & - dBasisdx = 0._dp ! avoid unitialized stuff depending on coordinate dimension... - - retval = ElementInfoVec_ComputePElementBasis(Element,Nodes,nc,u,v,w,detJ,nbmax,Basis,& - uWrk,vWrk,wWrk,BasisWrk,dBasisdxWrk,DetJWrk,LtoGmapsWrk,dBasisdx) - END FUNCTION ElementInfoVec - - FUNCTION ElementInfoVec_ComputePElementBasis(Element, Nodes, nc, u, v, w, DetJ, nbmax, Basis, & - uWrk, vWrk, wWrk, BasisWrk, dBasisdxWrk, & - DetJWrk, LtoGmapsWrk, dBasisdx) RESULT(retval) - IMPLICIT NONE - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Element nodal coordinates. - INTEGER, INTENT(IN) :: nc !< Number of local coordinates to compute values of the basis function - REAL(KIND=dp), POINTER CONTIG :: u(:) !< 1st local coordinates at which to calculate the basis function. - REAL(KIND=dp), POINTER CONTIG :: v(:) !< 2nd local coordinates. - REAL(KIND=dp), POINTER CONTIG :: w(:) !< 3rd local coordinates. - REAL(KIND=dp) CONTIG, INTENT(OUT) :: detJ(:) !< Square roots of determinants of element coordinate system metric at coordinates - INTEGER, INTENT(IN) :: nbmax !< Maximum number of basis functions to compute - REAL(KIND=dp) CONTIG :: Basis(:,:) !< Basis function values at (u,v,w) - ! Internal work arrays - REAL(KIND=dp) :: uWrk(VECTOR_BLOCK_LENGTH), vWrk(VECTOR_BLOCK_LENGTH), wWrk(VECTOR_BLOCK_LENGTH) - REAL(KIND=dp) :: BasisWrk(VECTOR_BLOCK_LENGTH,nbmax) - REAL(KIND=dp) :: dBasisdxWrk(VECTOR_BLOCK_LENGTH,nbmax,3) - REAL(KIND=dp) :: DetJWrk(VECTOR_BLOCK_LENGTH) - REAL(KIND=dp) :: LtoGMapsWrk(VECTOR_BLOCK_LENGTH,3,3) - REAL(KIND=dp) CONTIG, OPTIONAL :: dBasisdx(:,:,:) !< Global first derivatives of basis functions at (u,v,w) - LOGICAL :: retval !< If .FALSE. element is degenerate. or if local storage allocation fails - - - !------------------------------------------------------------------------------ - ! Local variables - !------------------------------------------------------------------------------ - INTEGER :: EdgeDegree(H1Basis_MaxPElementEdges), & - FaceDegree(H1Basis_MaxPElementFaces), & - EdgeDirection(H1Basis_MaxPElementEdgeNodes,H1Basis_MaxPElementEdges), & - FaceDirection(H1Basis_MaxPElementFaceNodes,H1Basis_MaxPElementFaces) - - INTEGER :: cdim, dim, i, j, k, l, ll, lln, ncl, ip, n, p, nb, & - nbp, nbq, nbdxp, allocstat, ncpad, EdgeMaxDegree, FaceMaxDegree - - - LOGICAL :: invertBubble, elem - -!DIR$ ATTRIBUTES ALIGN:64::EdgeDegree, FaceDegree -!DIR$ ATTRIBUTES ALIGN:64::EdgeDirection, FaceDirection -!DIR$ ASSUME_ALIGNED uWrk:64, vWrk:64, wWrk:64, BasisWrk:64, dBasisdxWrk:64, DetJWrk:64, LtoGMapsWrk:64 - - retval = .TRUE. - n = Element % TYPE % NumberOfNodes - dim = Element % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - - dBasisdxWrk = 0._dp ! avoid unitialized stuff depending on coordinate dimension... - - ! Block the computation for large values of input points - DO ll=1,nc,VECTOR_BLOCK_LENGTH - lln = MIN(ll+VECTOR_BLOCK_LENGTH-1,nc) - ncl = lln-ll+1 - - ! Set number of computed basis functions - nbp = 0 - nbdxp = 0 - - ! Block copy input - uWrk(1:ncl) = u(ll:lln) - IF (cdim > 1) THEN - vWrk(1:ncl) = v(ll:lln) - END IF - IF (cdim > 2) THEN - wWrk(1:ncl) = w(ll:lln) - END IF - - ! Compute local p element basis - SELECT CASE (Element % Type % ElementCode) - ! Element: LINE - CASE (202) - ! Compute nodal basis - CALL H1Basis_LineNodal(ncl, uWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dLineNodal(ncl, uWrk, nbmax, dBasisdxWrk, nbdxp) - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! For first round of blocked loop, compute edge direction - IF (ll==1) THEN - ! Compute P from bubble dofs - P = Element % BDOFS + 1 - - IF (Element % PDefs % isEdge .AND. & - Element % NodeIndexes(1)> Element % NodeIndexes(2)) THEN - invertBubble = .TRUE. - ELSE - invertBubble = .FALSE. - END IF - END IF - - CALL H1Basis_LineBubbleP(ncl, uWrk, P, nbmax, BasisWrk, nbp, invertBubble) - CALL H1Basis_dLineBubbleP(ncl, uWrk, P, nbmax, dBasisdxWrk, nbdxp, invertBubble) - END IF - - ! Element: TRIANGLE - CASE (303) - ! Compute nodal basis - CALL H1Basis_TriangleNodalP(ncl, uWrk, vWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dTriangleNodalP(ncl, uWrk, vWrk, nbmax, dBasisdxWrk, nbdxp) - - IF (ASSOCIATED( Element % EdgeIndexes)) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - CALL GetElementMeshEdgeInfo(CurrentModel % Solver % Mesh, & - Element, EdgeDegree, EdgeDirection, EdgeMaxDegree) - END IF - - ! Compute basis function values - IF (EdgeMaxDegree>1 ) THEN - nbq = nbp + SUM(EdgeDegree(1:3)-1) - IF(nbmax >= nbq ) THEN - CALL H1Basis_TriangleEdgeP(ncl, uWrk, vWrk, EdgeDegree, nbmax, BasisWrk, & - nbp, EdgeDirection) - CALL H1Basis_dTriangleEdgeP(ncl, uWrk, vWrk, EdgeDegree, nbmax, dBasisdxWrk, & - nbdxp, EdgeDirection) - END IF - END IF - END IF - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - ! Compute P from bubble dofs - P = CEILING( ( 3.0d0+SQRT(1.0d0+8.0d0*(Element % BDOFS)) ) / 2.0d0 - AEPS) - - IF (Element % PDefs % isEdge) THEN - ! Get 2D face direction - CALL H1Basis_GetFaceDirection(Element % Type % ElementCode, & - 1, & - Element % NodeIndexes, & - FaceDirection) - END IF - END IF - IF (Element % PDefs % isEdge) THEN - CALL H1Basis_TriangleBubbleP(ncl, uWrk, vWrk, P, nbmax, BasisWrk, nbp, & - FaceDirection(1:3,1)) - CALL H1Basis_dTriangleBubbleP(ncl, uWrk, vWrk, P, nbmax, dBasisdxWrk, nbdxp, & - FaceDirection(1:3,1)) - ELSE - CALL H1Basis_TriangleBubbleP(ncl, uWrk, vWrk, P, nbmax, BasisWrk, nbp) - CALL H1Basis_dTriangleBubbleP(ncl, uWrk, vWrk, P, nbmax, dBasisdxWrk, nbdxp) - END IF - END IF - - ! QUADRILATERAL - CASE (404) - ! Compute nodal basis - CALL H1Basis_QuadNodal(ncl, uWrk, vWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dQuadNodal(ncl, uWrk, vWrk, nbmax, dBasisdxWrk, nbdxp) - - IF (ASSOCIATED( Element % EdgeIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - CALL GetElementMeshEdgeInfo(CurrentModel % Solver % Mesh, & - Element, EdgeDegree, EdgeDirection, EdgeMaxDegree) - END IF - - ! Compute basis function values - IF (EdgeMaxDegree > 1) THEN - nbq = nbp + SUM(EdgeDegree(1:4)-1) - IF(nbmax >= nbq) THEN - CALL H1Basis_QuadEdgeP(ncl, uWrk, vWrk, EdgeDegree, nbmax, BasisWrk, nbp, & - EdgeDirection) - CALL H1Basis_dQuadEdgeP(ncl, uWrk, vWrk, EdgeDegree, nbmax, dBasisdxWrk, nbdxp, & - EdgeDirection) - END IF - END IF - END IF - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - ! Compute P from bubble dofs - P = CEILING( ( 5.0d0+SQRT(1.0d0+8.0d0*(Element % BDOFS)) ) / 2.0d0 - AEPS ) - - IF (Element % PDefs % isEdge) THEN - ! Get 2D face direction - CALL H1Basis_GetFaceDirection(Element % Type % ElementCode, & - 1, & - Element % NodeIndexes, & - FaceDirection) - END IF - END IF - - IF (Element % PDefs % isEdge) THEN - CALL H1Basis_QuadBubbleP(ncl, uWrk, vWrk, P, nbmax, BasisWrk, nbp, & - FaceDirection(1:4,1)) - CALL H1Basis_dQuadBubbleP(ncl, uWrk, vWrk, P, nbmax, dBasisdxWrk, nbdxp, & - FaceDirection(1:4,1)) - ELSE - CALL H1Basis_QuadBubbleP(ncl, uWrk, vWrk, P, nbmax, BasisWrk, nbp) - CALL H1Basis_dQuadBubbleP(ncl, uWrk, vWrk, P, nbmax, dBasisdxWrk, nbdxp) - END IF - END IF - - ! TETRAHEDRON - CASE (504) - ! Compute nodal basis - CALL H1Basis_TetraNodalP(ncl, uWrk, vWrk, wWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dTetraNodalP(ncl, uWrk, vWrk, wWrk, nbmax, dBasisdxWrk, nbdxp) - - IF (ASSOCIATED( Element % EdgeIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - ! Get polynomial degree of each edge - EdgeMaxDegree = 0 - IF( CurrentModel % Solver % Mesh % MaxEdgeDofs == 0 ) THEN - CONTINUE - ELSE IF (CurrentModel % Solver % Mesh % MinEdgeDOFs == & - CurrentModel % Solver % Mesh % MaxEdgeDOFs) THEN - EdgeMaxDegree = Element % BDOFs+1 - EdgeDegree(1:Element % Type % NumberOfFaces) = EdgeMaxDegree - ELSE - DO i=1,6 - EdgeDegree(i) = CurrentModel % Solver % & - Mesh % Edges( Element % EdgeIndexes(i) ) % BDOFs + 1 - EdgeMaxDegree = MAX(EdgeDegree(i),EdgeMaxDegree) - END DO - END IF - - ! Tetrahedral directions are enforced by tetra element types - IF (EdgeMaxDegree > 1) THEN - CALL H1Basis_GetTetraEdgeDirection(Element % PDefs % TetraType, EdgeDirection) - END IF - END IF - - ! Compute basis function values - IF (EdgeMaxDegree > 1) THEN - nbq = nbp + SUM(EdgeDegree(1:6)-1) - IF(nbmax >= nbq) THEN - CALL H1Basis_TetraEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, BasisWrk, nbp, & - EdgeDirection) - CALL H1Basis_dTetraEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, dBasisdxWrk, nbdxp, & - EdgeDirection) - END IF - END IF - END IF - - IF (ASSOCIATED( Element % FaceIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! face directions - IF (ll==1) THEN - ! Get polynomial degree of each face - FaceMaxDegree = 0 - - IF( CurrentModel % Solver % Mesh % MaxFaceDofs == 0 ) THEN - CONTINUE - ELSE IF (CurrentModel % Solver % Mesh % MinFaceDOFs == & - CurrentModel % Solver % Mesh % MaxFaceDOFs) THEN - FaceMaxDegree = CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(1) ) % PDefs % P - FaceDegree(1:Element % Type % NumberOfFaces) = FaceMaxDegree - ELSE - DO i=1,4 - IF (CurrentModel % Solver % Mesh % & - Faces( Element % FaceIndexes(i) ) % BDOFs /= 0) THEN - FaceDegree(i) = CurrentModel % Solver % Mesh % & - Faces( Element % FaceIndexes(i) ) % PDefs % P - FaceMaxDegree = MAX(FaceDegree(i), FaceMaxDegree) - ELSE - FaceDegree(i) = 0 - END IF - END DO - END IF - - IF (FaceMaxDegree > 1) THEN - CALL H1Basis_GetTetraFaceDirection(Element % PDefs % TetraType, FaceDirection) - END IF - END IF - - ! Compute basis function values - IF (FaceMaxDegree>1 ) THEN - nbq = nbp - DO i=1,4 - DO j=0,FaceDegree(i) - nbq = nbq + MAX(FaceDegree(i)-j-2,0) - END DO - END DO - - IF (nbmax >= nbq ) THEN - CALL H1Basis_TetraFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, BasisWrk, nbp, & - FaceDirection) - CALL H1Basis_dTetraFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, dBasisdxWrk, nbdxp, & - FaceDirection) - END IF - END IF - END IF - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! Compute P based on bubble dofs - nb = Element % BDOFs - p = CEILING( 1/3._dp*(81*nb+3*SQRT(-3._dp+729*nb**2))**(1/3._dp) + & - 1d0/(81*nb+3*SQRT(-3._dp+729*nb**2))**(1/3._dp)+2 - AEPS ) - - CALL H1Basis_TetraBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, BasisWrk, nbp) - CALL H1Basis_dTetraBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, dBasisdxWrk, nbdxp) - END IF - - ! TEMPORARY NONVECTORIZED PYRAMID - CASE (605) -BLOCK - INTEGER :: F, locali, localj, nb, q, tmp(4), direction(4) - LOGICAL :: invert - TYPE(Element_t), POINTER :: Face, Edge - - dBasisdxWrk(1:ncl,:,:) = 0.0d0 - BasisWrk(1:ncl,:) = 0.0d0 - DO l=1,ncl - CALL NodalBasisFunctions(5, BasisWrk(l,:), element, uWrk(l), vWrk(l), wWrk(l)) - CALL NodalFirstDerivatives(5, dBasisdxWrk(l,:,:), element, uWrk(l), vWrk(l), wWrk(l) ) - - q = 5 - - ! Edges of P Pyramid - IF (ASSOCIATED( Element % EdgeIndexes ) ) THEN - ! For each edge in wedge, calculate values of edge functions - DO i=1,8 - Edge => CurrentModel % Solver % Mesh % Edges( Element % EdgeIndexes(i) ) - - ! Do not solve edge dofs, if there is not any - IF (Edge % BDOFs <= 0) CYCLE - - ! Get local indexes of current edge - tmp(1:2) = getPyramidEdgeMap(i) - locali = tmp(1) - localj = tmp(2) - - ! Determine edge direction - invert = .FALSE. - - ! Invert edge if local first node has greater global index than second one - IF ( Element % NodeIndexes(locali) > Element % NodeIndexes(localj) ) invert = .TRUE. - - ! For each DOF in edge calculate values of edge functions - ! and their derivatives for edge=i and i=k+1 - DO k=1,Edge % BDOFs - IF ( q >= SIZE(BasisWrk,2) ) CYCLE - q = q + 1 - - ! Get values of edge basis functions and their derivatives - BasisWrk(l,q) = PyramidEdgePBasis(i,k+1,uwrk(l),vwrk(l),wwrk(l),invert) - dBasisdxWrk(l,q,1:3) = dPyramidEdgePBasis(i,k+1,uwrk(l),vwrk(l),wwrk(l),invert) - END DO - END DO - END IF - - ! Faces of P Pyramid - IF ( ASSOCIATED( Element % FaceIndexes ) ) THEN - ! For each face in pyramid, calculate values of face functions - DO F=1,5 - Face => CurrentModel % Solver % Mesh % Faces( Element % FaceIndexes(F) ) - - ! Do not solve face dofs, if there is not any - IF ( Face % BDOFs <= 0) CYCLE - - ! Get face p - p = Face % PDefs % P - - ! Handle triangle and square faces separately - SELECT CASE(F) - CASE (1) - direction = 0 - ! Get global direction vector for enforcing parity - tmp(1:4) = getPyramidFaceMap(F) - direction(1:4) = getSquareFaceDirection( Element, tmp(1:4) ) - - ! For each face calculate values of functions from index - ! pairs i,j=2,..,p-2 i+j=4,..,p - DO i=2,p-2 - DO j=2,p-i - IF ( q >= SIZE(BasisWrk,2) ) CYCLE - q = q + 1 - - BasisWrk(l,q) = PyramidFacePBasis(F,i,j,uwrk(l),vwrk(l),wwrk(l),direction) - dBasisdxWrk(l,q,:) = dPyramidFacePBasis(F,i,j,uwrk(l),vwrk(l),wwrk(l),direction) - END DO - END DO - - CASE (2,3,4,5) - direction = 0 - ! Get global direction vector for enforcing parity - tmp(1:4) = getPyramidFaceMap(F) - direction(1:3) = getTriangleFaceDirection( Element, tmp(1:3) ) - - ! For each face calculate values of functions from index - ! pairs i,j=0,..,p-3 i+j=0,..,p-3 - DO i=0,p-3 - DO j=0,p-i-3 - IF ( q >= SIZE(BasisWrk,2) ) CYCLE - q = q + 1 - - BasisWrk(l,q) = PyramidFacePBasis(F,i,j,uwrk(l),vwrk(l),wwrk(l),direction) - dBasisdxWrk(l,q,:) = dPyramidFacePBasis(F,i,j,uwrk(l),vwrk(l),wwrk(l),direction) - END DO - END DO - END SELECT - END DO - END IF - - ! Bubbles of P Pyramid - IF (Element % BDOFs > 0) THEN - ! Get element p - p = Element % PDefs % p - nb = MAX( GetBubbleDOFs(Element, p), Element % BDOFs ) - p=CEILING(1/3d0*(81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+1d0/ & - (81*nb+3*SQRT(-3d0+729*nb**2))**(1/3d0)+2 - AEPS) - - ! Calculate value of bubble functions from indexes - ! i,j,k=0,..,p-4 i+j+k=0,..,p-4 - DO i=0,p-4 - DO j=0,p-i-4 - DO k=0,p-i-j-4 - IF ( q >= SIZE(BasisWrk,2)) CYCLE - q = q + 1 - - BasisWrk(l,q) = PyramidBubblePBasis(i,j,k,uwrk(l),vwrk(l),wwrk(l)) - dBasisdxWrk(l,q,:) = dPyramidBubblePBasis(i,j,k,uwrk(l),vwrk(l),wwrk(l)) - END DO - END DO - END DO - END IF - END DO - - nbp = q -!------------------------------------------------------------------------------ -END BLOCK - - - ! WEDGE - CASE (706) - ! Compute nodal basis - CALL H1Basis_WedgeNodalP(ncl, uWrk, vWrk, wWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dWedgeNodalP(ncl, uWrk, vWrk, wWrk, nbmax, dBasisdxWrk, nbdxp) - - IF (ASSOCIATED( Element % EdgeIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - CALL GetElementMeshEdgeInfo(CurrentModel % Solver % Mesh, & - Element, EdgeDegree, EdgeDirection, EdgeMaxDegree) - END IF - - ! Compute basis function values - IF (EdgeMaxDegree > 1)THEN - nbq = nbp+SUM(EdgeDegree(1:9)-1) - IF(nbmax >= nbq) THEN - CALL H1Basis_WedgeEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, BasisWrk, nbp, & - EdgeDirection) - CALL H1Basis_dWedgeEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, dBasisdxWrk, nbdxp, & - EdgeDirection) - END IF - END IF - END IF - - IF (ASSOCIATED( Element % FaceIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! face directions - IF (ll==1) THEN - CALL GetElementMeshFaceInfo(CurrentModel % Solver % Mesh, & - Element, FaceDegree, FaceDirection, FaceMaxDegree) - END IF - - ! Compute basis function values - IF (FaceMaxDegree > 1 ) THEN - nbq = nbp - ! Triangle faces - DO i=1,2 - DO j=0,FaceDegree(i)-3 - nbq = nbq + MAX(FaceDegree(i)-j-2,0) - END DO - END DO - ! Square faces - DO i=3,5 - DO j=2,FaceDegree(i)-2 - nbq = nbq + MAX(FaceDegree(i)-j-1,0) - END DO - END DO - - IF(nbmax >= nbq) THEN - CALL H1Basis_WedgeFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, BasisWrk, nbp, & - FaceDirection) - CALL H1Basis_dWedgeFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, dBasisdxWrk, nbdxp, & - FaceDirection) - END IF - END IF - END IF - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! Compute P from bubble dofs - P=CEILING(1/3d0*(81*(Element % BDOFS) + & - 3*SQRT(-3d0+729*(Element % BDOFS)**2))**(1/3d0) + & - 1d0/(81*(Element % BDOFS)+ & - 3*SQRT(-3d0+729*(Element % BDOFS)**2))**(1/3d0)+3 - AEPS) - - CALL H1Basis_WedgeBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, BasisWrk, nbp) - CALL H1Basis_dWedgeBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, dBasisdxWrk, nbdxp) - END IF - - ! HEXAHEDRON - CASE (808) - ! Compute local basis - CALL H1Basis_BrickNodal(ncl, uWrk, vWrk, wWrk, nbmax, BasisWrk, nbp) - ! Compute local first derivatives - CALL H1Basis_dBrickNodal(ncl, uWrk, vWrk, wWrk, nbmax, dBasisdxWrk, nbdxp) - - IF (ASSOCIATED( Element % EdgeIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! edge directions - IF (ll==1) THEN - CALL GetElementMeshEdgeInfo(CurrentModel % Solver % Mesh, & - Element, EdgeDegree, EdgeDirection, EdgeMaxDegree) - END IF - - ! Compute basis function values - IF (EdgeMaxDegree > 1) THEN - nbq = nbp + SUM(EdgeDegree(1:12)-1) - IF(nbmax >= nbq) THEN - CALL H1Basis_BrickEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, BasisWrk, nbp, & - EdgeDirection) - CALL H1Basis_dBrickEdgeP(ncl, uWrk, vWrk, wWrk, EdgeDegree, nbmax, dBasisdxWrk, nbdxp, & - EdgeDirection) - END IF - END IF - END IF - - - IF (ASSOCIATED( Element % FaceIndexes )) THEN - ! For first round of blocked loop, compute polynomial degrees and - ! face directions - IF (ll==1) THEN - CALL GetElementMeshFaceInfo(CurrentModel % Solver % Mesh, & - Element, FaceDegree, FaceDirection, FaceMaxDegree) - END IF - - ! Compute basis function values - IF (FaceMaxDegree > 1) THEN - nbq = nbp - DO i=1,6 - DO j=2,FaceDegree(i) - nbq = nbq + MAX(FaceDegree(i)-j-1,0) - END DO - END DO - - IF(nbmax >= nbq) THEN - CALL H1Basis_BrickFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, BasisWrk, nbp, & - FaceDirection) - CALL H1Basis_dBrickFaceP(ncl, uWrk, vWrk, wWrk, FaceDegree, nbmax, dBasisdxWrk, nbdxp, & - FaceDirection) - END IF - END IF - END IF - - - ! Element bubble functions - IF (Element % BDOFS > 0) THEN - ! Compute P from bubble dofs - P=CEILING(1/3d0*(81*Element % BDOFS + & - 3*SQRT(-3d0+729*Element % BDOFS**2))**(1/3d0) + & - 1d0/(81*Element % BDOFS+3*SQRT(-3d0+729*Element % BDOFS**2))**(1/3d0)+4 - AEPS) - CALL H1Basis_BrickBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, BasisWrk, nbp) - CALL H1Basis_dBrickBubbleP(ncl, uWrk, vWrk, wWrk, P, nbmax, dBasisdxWrk, nbdxp) - END IF - - - CASE DEFAULT - WRITE( Message, '(a,i4,a)' ) 'Vectorized basis for element: ', & - Element % TYPE % ElementCode, ' not implemented.' - CALL Error( 'ElementInfoVec', Message ) - CALL Fatal( 'ElementInfoVec', 'ElementInfoVec is still does not include pyramids.' ) - END SELECT - - ! Copy basis function values to global array - DO j=1,nbp - DO i=1,ncl - Basis(i+ll-1,j)=BasisWrk(i,j) - END DO - END DO - - !-------------------------------------------------------------- - ! Element (contravariant) metric and square root of determinant - !-------------------------------------------------------------- - elem = ElementMetricVec( Element, Nodes, ncl, nbp, DetJWrk, & - nbmax, dBasisdxWrk, LtoGMapsWrk ) - IF (.NOT. elem) THEN - retval = .FALSE. - RETURN - END IF - - !_ELMER_OMP_SIMD - DO i=1,ncl - DetJ(i+ll-1)=DetJWrk(i) - END DO - - ! Get global basis functions - !-------------------------------------------------------------- - ! First derivatives - IF (PRESENT(dBasisdx)) THEN -!DIR$ FORCEINLINE - CALL ElementInfoVec_ElementBasisToGlobal(ncl, nbp, nbmax, dBasisdxWrk, dim, cdim, LtoGMapsWrk, ll, dBasisdx) - END IF - END DO ! Block over Gauss points - - CONTAINS - - SUBROUTINE GetElementMeshEdgeInfo(Mesh, Element, EdgeDegree, EdgeDirection, EdgeMaxDegree) - IMPLICIT NONE - - TYPE(Mesh_t), INTENT(IN) :: Mesh - TYPE(Element_t), INTENT(IN) :: Element - INTEGER, INTENT(OUT) :: EdgeDegree(H1Basis_MaxPElementEdges), & - EdgeDirection(H1Basis_MaxPElementEdgeNodes,H1Basis_MaxPElementEdges) - INTEGER, INTENT(OUT) :: EdgeMaxDegree - INTEGER :: i - - EdgeMaxDegree = 0 - - IF( Mesh % MaxEdgeDofs == 0 ) THEN - CONTINUE - - ELSE IF (Mesh % MinEdgeDOFs == Mesh % MaxEdgeDOFs) THEN - EdgeDegree(1:Element % Type % NumberOfEdges) = Mesh % MaxEdgeDOFs + 1 - EdgeMaxDegree = Mesh % MaxEdgeDOFs + 1 - ELSE - ! Get polynomial degree of each edge separately -!DIR$ LOOP COUNT MAX=12 - DO i=1,Element % Type % NumberOfEdges - EdgeDegree(i) = Mesh % Edges( Element % EdgeIndexes(i) ) % BDOFs + 1 - EdgeMaxDegree = MAX(EdgeDegree(i), EdgeMaxDegree) - END DO - END IF - - ! Get edge directions if needed - IF (EdgeMaxDegree > 1) THEN - CALL H1Basis_GetEdgeDirection(Element % Type % ElementCode, & - Element % Type % NumberOfEdges, & - Element % NodeIndexes, & - EdgeDirection) - END IF - END SUBROUTINE GetElementMeshEdgeInfo - - SUBROUTINE GetElementMeshFaceInfo(Mesh, Element, FaceDegree, FaceDirection, FaceMaxDegree) - IMPLICIT NONE - - TYPE(Mesh_t), INTENT(IN) :: Mesh - TYPE(Element_t), INTENT(IN) :: Element - INTEGER, INTENT(OUT) :: FaceDegree(H1Basis_MaxPElementFaces), & - FaceDirection(H1Basis_MaxPElementFaceNodes,H1Basis_MaxPElementFaces) - INTEGER, INTENT(OUT) :: FaceMaxDegree - INTEGER :: i - - ! Get polynomial degree of each face - FaceMaxDegree = 0 - - IF( Mesh % MaxFaceDofs == 0 ) THEN - CONTINUE - - ELSE IF (Mesh % MinFaceDOFs == Mesh % MaxFaceDOFs) THEN - FaceMaxDegree = Mesh % Faces( Element % FaceIndexes(1) ) % PDefs % P - FaceDegree(1:Element % Type % NumberOfFaces) = FaceMaxDegree - ELSE -!DIR$ LOOP COUNT MAX=6 - DO i=1,Element % Type % NumberOfFaces - IF (Mesh % Faces( Element % FaceIndexes(i) ) % BDOFs /= 0) THEN - FaceDegree(i) = Mesh % Faces( Element % FaceIndexes(i) ) % PDefs % P - FaceMaxDegree = MAX(FaceDegree(i), FaceMaxDegree) - ELSE - FaceDegree(i) = 0 - END IF - END DO - END IF - - ! Get face directions - IF (FaceMaxDegree > 1) THEN - CALL H1Basis_GetFaceDirection(Element % Type % ElementCode, & - Element % Type % NumberOfFaces, & - Element % NodeIndexes, & - FaceDirection) - END IF - END SUBROUTINE GetElementMeshFaceInfo -!------------------------------------------------------------------------------ - END FUNCTION ElementInfoVec_ComputePElementBasis -!------------------------------------------------------------------------------ - - SUBROUTINE ElementInfoVec_ElementBasisToGlobal(npts, nbasis, nbmax, dLBasisdx, dim, cdim, LtoGMap, offset, dBasisdx) - IMPLICIT NONE - - INTEGER, INTENT(IN) :: npts - INTEGER, INTENT(IN) :: nbasis - INTEGER, INTENT(IN) :: nbmax - REAL(KIND=dp), INTENT(IN) :: dLBasisdx(VECTOR_BLOCK_LENGTH,nbmax,3) - INTEGER, INTENT(IN) :: dim - INTEGER, INTENT(IN) :: cdim - REAL(KIND=dp), INTENT(IN) :: LtoGMap(VECTOR_BLOCK_LENGTH,3,3) - INTEGER, INTENT(IN) :: offset - REAL(KIND=dp) CONTIG :: dBasisdx(:,:,:) - - INTEGER :: i, j, l -!DIR$ ASSUME_ALIGNED dLBasisdx:64, LtoGMap:64 - - ! Map local basis function to global - SELECT CASE (dim) - CASE(1) - !DIR$ LOOP COUNT MAX=3 - DO j=1,cdim - DO i=1,nbasis - !_ELMER_OMP_SIMD - DO l=1,npts - dBasisdx(l+offset-1,i,j) = dLBasisdx(l,i,1)*LtoGMap(l,j,1) - END DO - END DO - END DO - CASE(2) - !DIR$ LOOP COUNT MAX=3 - DO j=1,cdim - DO i=1,nbasis - !_ELMER_OMP_SIMD - DO l=1,npts - ! Map local basis function to global - dBasisdx(l+offset-1,i,j) = dLBasisdx(l,i,1)*LtoGMap(l,j,1)+ & - dLBasisdx(l,i,2)*LtoGMap(l,j,2) - END DO - END DO - END DO - CASE(3) - !DIR$ LOOP COUNT MAX=3 - DO j=1,cdim - DO i=1,nbasis - !_ELMER_OMP_SIMD - DO l=1,npts - ! Map local basis function to global - dBasisdx(l+offset-1,i,j) = dLBasisdx(l,i,1)*LtoGMap(l,j,1)+ & - dLBasisdx(l,i,2)*LtoGMap(l,j,2)+ & - dLBasisdx(l,i,3)*LtoGMap(l,j,3) - END DO - END DO - END DO - END SELECT - - END SUBROUTINE ElementInfoVec_ElementBasisToGlobal - - -!------------------------------------------------------------------------------ -!> Returns just the size of the element at its center. -!> providing a more economical way than calling ElementInfo. -!------------------------------------------------------------------------------ - FUNCTION ElementSize( Element, Nodes ) RESULT ( detJ ) - - TYPE(Element_t) :: Element - TYPE(Nodes_t) :: Nodes - REAL(KIND=dp) :: detJ - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp), ALLOCATABLE :: Basis(:) - INTEGER :: n,family - LOGICAL :: Stat - - - family = Element % TYPE % ElementCode / 100 - n = Element % TYPE % NumberOfNodes - ALLOCATE( Basis(n) ) - - SELECT CASE ( family ) - - CASE ( 1 ) - DetJ = 1.0_dp - RETURN - - CASE ( 2 ) - u = 0.0_dp - v = 0.0_dp - - CASE ( 3 ) - u = 0.5_dp - v = 0.5_dp - - CASE ( 4 ) - u = 0.0_dp - v = 0.0_dp - - CASE ( 5 ) - u = 0.5_dp - v = 0.5_dp - w = 0.5_dp - - CASE ( 8 ) - u = 0.0_dp - v = 0.0_dp - w = 0.0_dp - - CASE DEFAULT - CALL Fatal('ElementSize','Not implemented for elementtype') - - END SELECT - - Stat = ElementInfo( Element, Nodes, u, v, w, detJ, Basis ) - - END FUNCTION ElementSize -!------------------------------------------------------------------------------ - - -!---------------------------------------------------------------------------------- -!> Return H(div)-conforming face element basis function values and their divergence -!> with respect to the reference element coordinates at a given point on the -!> reference element. Here the basis for a real element K is constructed by -!> transforming the basis functions defined on the reference element k via the -!> Piola transformation. The data for performing the Piola transformation is also returned. -!> Note that the reference element is chosen as in the p-approximation so that -!> the reference element edges/faces have the same length/area. This choice simplifies -!> the associated assembly procedure. -!> With giving the optional argument ApplyPiolaTransform = .TRUE., this function -!> also performs the Piola transform, so that the basis functions and their spatial -!> div as defined on the physical element are returned. -!> The implementation is not yet complete as all element shapes are not supported. -!--------------------------------------------------------------------------------- - RECURSIVE FUNCTION FaceElementInfo( Element, Nodes, u, v, w, F, detF, & - Basis, FBasis, DivFBasis, BDM, Dual, BasisDegree, ApplyPiolaTransform) RESULT(stat) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Data corresponding to the classic element nodes - REAL(KIND=dp) :: u !< 1st reference element coordinate at which the basis functions are evaluated - REAL(KIND=dp) :: v !< 2nd reference element coordinate - REAL(KIND=dp) :: w !< 3rd reference element coordinate - REAL(KIND=dp), OPTIONAL :: F(3,3) !< The gradient F=Grad f, with f the element map f:k->K - REAL(KIND=dp) :: detF !< The determinant of the gradient matrix F - REAL(KIND=dp) :: Basis(:) !< Standard nodal basis functions evaluated at (u,v,w) - REAL(KIND=dp) :: FBasis(:,:) !< Face element basis functions b spanning the reference element space - REAL(KIND=dp), OPTIONAL :: DivFBasis(:) !< The divergence of basis functions with respect to the local coordinates - LOGICAL, OPTIONAL :: BDM !< If .TRUE., a basis for BDM space is constructed - LOGICAL, OPTIONAL :: Dual !< If .TRUE., create an alternate dual basis - INTEGER, OPTIONAL :: BasisDegree(:) !< This a dummy parameter at the moment - LOGICAL, OPTIONAL :: ApplyPiolaTransform !< If .TRUE., perform the Piola transform so that, instead of b - !< and Div b, return B(f(p)) and (div B)(f(p)) with B(x) the basis - !< functions on the physical element and div the spatial divergence operator. - LOGICAL :: Stat !< Should be .FALSE. for a degenerate element but this is not yet checked -!----------------------------------------------------------------------------------------------------------------- -! Local variables -!------------------------------------------------------------------------------------------------------------ - TYPE(Mesh_t), POINTER :: Mesh - INTEGER, POINTER :: EdgeMap(:,:), FaceMap(:,:), Ind(:) - INTEGER :: SquareFaceMap(4) - INTEGER :: DOFs - INTEGER :: n, dim, q, i, j, k, ni, nj, nk, I1, I2 - INTEGER :: FDofMap(4,3), DofsPerFace, FaceIndices(4) - REAL(KIND=dp) :: LF(3,3) - REAL(KIND=dp) :: DivBasis(12) ! Note the hard-coded size, alter if new elements are added - REAL(KIND=dp) :: dLbasisdx(MAX(SIZE(Nodes % x),SIZE(Basis)),3), S, D1, D2 - REAL(KIND=dp) :: BDMBasis(12,3), BDMDivBasis(12), WorkBasis(2,3), WorkDivBasis(2) - - LOGICAL :: RevertSign(4), CreateBDMBasis, Parallel - LOGICAL :: CreateDualBasis - LOGICAL :: PerformPiolaTransform -!----------------------------------------------------------------------------------------------------- - Mesh => CurrentModel % Solver % Mesh - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - - !-------------------------------------------------------------------- - ! Check whether BDM or dual basis functions should be created and - ! whether the Piola transform is already applied within this function. - !--------------------------------------------------------------------- - CreateBDMBasis = .FALSE. - IF ( PRESENT(BDM) ) CreateBDMBasis = BDM - CreateDualBasis = .FALSE. - IF ( PRESENT(Dual) ) CreateDualBasis = Dual - PerformPiolaTransform = .FALSE. - IF ( PRESENT(ApplyPiolaTransform) ) PerformPiolaTransform = ApplyPiolaTransform - !----------------------------------------------------------------------------------------------------- - stat = .TRUE. - Basis = 0.0d0 - FBasis = 0.0d0 - DivFBasis = 0.0d0 - DivBasis = 0.0d0 - LF = 0.0d0 - - dLbasisdx = 0.0d0 - n = Element % TYPE % NumberOfNodes - dim = Element % TYPE % DIMENSION - - IF ( Element % TYPE % ElementCode == 101 ) THEN - detF = 1.0d0 - Basis(1) = 1.0d0 - RETURN - END IF - - !----------------------------------------------------------------------- - ! The standard nodal basis functions on the reference element and - ! their derivatives with respect to the local coordinates. These define - ! the mapping of the reference element to an actual element on the - ! background mesh but are not the basis functions for face element approximation. - ! Remark: Using reference elements having the faces of the same area - ! simplifies the implementation of element assembly procedures. - !----------------------------------------------------------------------- - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3) - DO q=1,n - Basis(q) = TriangleNodalPBasis(q, u, v) - dLBasisdx(q,1:2) = dTriangleNodalPBasis(q, u, v) - END DO - CASE(4) - DO q=1,n - Basis(q) = QuadNodalPBasis(q, u, v) - dLBasisdx(q,1:2) = dQuadNodalPBasis(q, u, v) - END DO - CASE(5) - DO q=1,n - Basis(q) = TetraNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dTetraNodalPBasis(q, u, v, w) - END DO - CASE DEFAULT - CALL Fatal('ElementDescription::FaceElementInfo','Unsupported element type') - END SELECT - - !----------------------------------------------------------------------- - ! Get data for performing the Piola transformation... - !----------------------------------------------------------------------- - stat = PiolaTransformationData(n, Element, Nodes, LF, detF, dLBasisdx) - !------------------------------------------------------------------------ - ! ... in order to define the basis for the element space X(K) via - ! applying the Piola transformation as - ! X(K) = { B | B = 1/(det F) F b(f^{-1}(x)) } - ! with b giving the face element basis function on the reference element k, - ! f mapping k to the actual element K, i.e. K = f(k) and F = Grad f. This - ! function returns the local basis functions b and their divergence (with respect - ! to local coordinates) evaluated at the integration point. The effect of - ! the Piola transformation need to be considered when integrating, so we - ! shall return also the values of F and det F. - ! - ! The construction of face element bases could be done in an alternate way for - ! triangles and tetrahedra, while the chosen approach has the benefit that - ! it generalizes to other cases. For example general quadrilaterals may now - ! be handled in the same way. - !--------------------------------------------------------------------------- - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3) - !---------------------------------------------------------------- - ! Note that the global orientation of face normal is taken to be - ! n = t x e_z where the tangent vector t is aligned with - ! the element edge and points towards the node that has - ! a larger global index. - !--------------------------------------------------------------- - EdgeMap => GetEdgeMap(3) - !EdgeMap => GetEdgeMap(GetElementFamily(Element)) - - !----------------------------------------------------------------------------------- - ! Check first whether a sign reversion will be needed as face dofs have orientation. - !----------------------------------------------------------------------------------- - CALL FaceElementOrientation(Element, RevertSign) - - IF (CreateBDMBasis) THEN - !---------------------------------------------------------------------------- - ! This is for the BDM space of degree k=1. - !---------------------------------------------------------------------------- - DOFs = 6 - DofsPerFace = 2 - !---------------------------------------------------------------------------- - ! First tabulate the basis functions in the default order. - !---------------------------------------------------------------------------- - ! Two basis functions defined on face 12: - !------------------------------------------------- - FBasis(1,1) = sqrt(3.0d0)/6.0d0 * (-sqrt(3.0d0) + u + v) - FBasis(1,2) = sqrt(3.0d0)/6.0d0 * (-sqrt(3.0d0) + 3.0d0 * u + v) - DivBasis(1) = sqrt(3.0d0)/3.0d0 - - FBasis(2,1) = sqrt(3.0d0)/6.0d0 * (sqrt(3.0d0) + u - v) - FBasis(2,2) = sqrt(3.0d0)/6.0d0 * (-sqrt(3.0d0) - 3.0d0 * u + v) - DivBasis(2) = sqrt(3.0d0)/3.0d0 - - ! Two basis functions defined on face 23: - - FBasis(3,1) = 1.0d0/(3.0d0+sqrt(3.0d0)) * (2.0d0+sqrt(3.0d0)+(2.0d0+sqrt(3.0d0))*u-(1.0d0+sqrt(3.0d0))*v) - FBasis(3,2) = 1.0d0/6.0d0 * ( -3.0d0+sqrt(3.0d0) ) * v - DivBasis(3) = sqrt(3.0d0)/3.0d0 - - FBasis(4,1) = 1.0d0/6.0d0 * (-3.0d0+sqrt(3.0d0)+(-3.0d0+sqrt(3.0d0))*u + 2.0d0*sqrt(3.0d0)*v) - FBasis(4,2) = 1.0d0/6.0d0 * ( 3.0d0+sqrt(3.0d0) ) * v - DivBasis(4) = sqrt(3.0d0)/3.0d0 - - - ! Two basis functions defined on face 31: - - FBasis(5,1) = 1.0d0/( 3.0d0+sqrt(3.0d0) ) * ( 1.0d0 - u - v - sqrt(3.0d0)*v ) - FBasis(5,2) = ( 3.0d0+2.0d0*sqrt(3.0d0) ) * v /(3.0d0*(1.0d0+sqrt(3.0d0))) - DivBasis(5) = sqrt(3.0d0)/3.0d0 - - FBasis(6,1) = 1.0d0/6.0d0 * (-3.0d0-sqrt(3.0d0)+(3.0d0+sqrt(3.0d0))*u + 2.0d0*sqrt(3.0d0)*v) - FBasis(6,2) = 1.0d0/6.0d0 * ( -3.0d0+sqrt(3.0d0) ) * v - DivBasis(6) = sqrt(3.0d0)/3.0d0 - - !----------------------------------------------------- - ! Now do the reordering and sign reversion: - !----------------------------------------------------- - DO q=1,3 - IF (RevertSign(q)) THEN - DO j=1,DofsPerFace - i = (q-1)*DofsPerFace + j - WorkBasis(j,1:2) = FBasis(i,1:2) - WorkDivBasis(j) = DivBasis(i) - END DO - i = 2*q - 1 - FBasis(i,1:2) = -WorkBasis(2,1:2) - DivBasis(i) = -WorkDivBasis(2) - i = 2*q - FBasis(i,1:2) = -WorkBasis(1,1:2) - DivBasis(i) = -WorkDivBasis(1) - END IF - END DO - - ELSE - DOFs = 3 - - FBasis(1,1) = SQRT(3.0d0)/6.0d0 * u - FBasis(1,2) = -0.5d0 + SQRT(3.0d0)/6.0d0 * v - DivBasis(1) = SQRT(3.0d0)/3.0d0 - IF (RevertSign(1)) THEN - FBasis(1,:) = -FBasis(1,:) - DivBasis(1) = -DivBasis(1) - END IF - - FBasis(2,1) = SQRT(3.0d0)/6.0d0 * (1.0d0 + u) - FBasis(2,2) = SQRT(3.0d0)/6.0d0 * v - DivBasis(2) = SQRT(3.0d0)/3.0d0 - IF (RevertSign(2)) THEN - FBasis(2,:) = -FBasis(2,:) - DivBasis(2) = -DivBasis(2) - END IF - - FBasis(3,1) = SQRT(3.0d0)/6.0d0 * (-1.0d0 + u) - FBasis(3,2) = SQRT(3.0d0)/6.0d0 * v - DivBasis(3) = SQRT(3.0d0)/3.0d0 - IF (RevertSign(3)) THEN - FBasis(3,:) = -FBasis(3,:) - DivBasis(3) = -DivBasis(3) - END IF - - END IF - - CASE(4) - DOFs = 6 - !-------------------------------------------------------------------- - ! Quadrilateral Arnold-Boffi-Falk (ABF) element basis of degree k=0 - !-------------------------------------------------------------------- - EdgeMap => GetEdgeMap(4) - SquareFaceMap(:) = (/ 1,2,3,4 /) - Ind => Element % Nodeindexes - - IF (.NOT. CreateDualBasis) THEN - !------------------------------------------------- - ! Four basis functions defined on the edges - !------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - FBasis(1,1) = 0.0d0 - FBasis(1,2) = -((-1.0d0 + v)*v)/4.0d0 - DivBasis(1) = (1.0d0 - 2*v)/4.0d0 - IF (nj This function returns data for performing the Piola transformation -!------------------------------------------------------------------------------------------------ - FUNCTION PiolaTransformationData(nn,Element,Nodes,F,DetF,dLBasisdx) RESULT(Success) -!------------------------------------------------------------------------------------------------- - INTEGER :: nn !< The number of classic nodes used in the element mapping - TYPE(Element_t) :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Data corresponding to the classic element nodes - REAL(KIND=dp) :: F(:,:) !< The gradient of the element mapping - REAL(KIND=dp) :: DetF !< The determinant of the gradient matrix (or the Jacobian matrix) - REAL(KIND=dp) :: dLBasisdx(:,:) !< Derivatives of nodal basis functions with respect to local coordinates - LOGICAL :: Success !< Could and should return .FALSE. if the element is degenerate -!----------------------------------------------------------------------------------------------------- -! Local variables -!------------------------------------------------------------------------------------------------- - REAL(KIND=dp), DIMENSION(:), POINTER :: x,y,z - INTEGER :: cdim,dim,n,i -!------------------------------------------------------------------------------------------------- - x => Nodes % x - y => Nodes % y - z => Nodes % z - - ! cdim = CoordinateSystemDimension() - n = MIN( SIZE(x), nn ) - dim = Element % TYPE % DIMENSION - - !------------------------------------------------------------------------------ - ! The gradient of the element mapping K = f(k), with k the reference element - !------------------------------------------------------------------------------ - F = 0.0d0 - DO i=1,dim - F(1,i) = SUM( x(1:n) * dLBasisdx(1:n,i) ) - F(2,i) = SUM( y(1:n) * dLBasisdx(1:n,i) ) - !IF (dim == 3) & - ! In addition to the case dim = 3, the following entries may be useful - ! with dim=2 when natural BCs in 3-D are handled. - F(3,i) = SUM( z(1:n) * dLBasisdx(1:n,i) ) - END DO - - SELECT CASE( dim ) - CASE (2) - DetF = F(1,1)*F(2,2) - F(1,2)*F(2,1) - CASE(3) - DetF = F(1,1) * ( F(2,2)*F(3,3) - F(2,3)*F(3,2) ) + & - F(1,2) * ( F(2,3)*F(3,1) - F(2,1)*F(3,3) ) + & - F(1,3) * ( F(2,1)*F(3,2) - F(2,2)*F(3,1) ) - END SELECT - - success = .TRUE. -!------------------------------------------------ - END FUNCTION PiolaTransformationData -!------------------------------------------------ - -!----------------------------------------------------------------------------------- -!> Get information about whether a sign reversion will be needed to obtain right -!> DOFs for face (vector) elements. If the sign is not reverted, the positive value of -!> the degree of freedom produces positive outward flux from the element through -!> the face handled. -!----------------------------------------------------------------------------------- -SUBROUTINE FaceElementOrientation(Element, RevertSign, FaceIndex, Nodes) -!----------------------------------------------------------------------------------- - IMPLICIT NONE - - TYPE(Element_t), INTENT(IN) :: Element !< A 3-D/2-D element having 2-D/1-D faces - LOGICAL, INTENT(OUT) :: RevertSign(:) !< Face-wise information about the sign reversions - INTEGER, OPTIONAL, INTENT(IN) :: FaceIndex !< Check just one face that is specified here - TYPE(Nodes_t), OPTIONAL :: Nodes !< An inactive variable related to code verification -!----------------------------------------------------------------------------------- - TYPE(Mesh_t), POINTER :: Mesh - LOGICAL :: Parallel - - INTEGER, POINTER :: FaceMap(:,:), Ind(:) - INTEGER, TARGET :: TetraFaceMap(4,3) - INTEGER :: FaceIndices(4) - INTEGER :: j, q, first_face, last_face - - ! Some inactive variables that were used in the code verification - LOGICAL :: RevertSign2(4), CheckSignReversions - INTEGER :: i, k, A, B, C, D - REAL(KIND=dp) :: t1(3), t2(3), m(3), e(3) -!----------------------------------------------------------------------------------- - RevertSign(:) = .FALSE. - - IF (PRESENT(FaceIndex)) THEN - first_face = FaceIndex - last_face = FaceIndex - ELSE - first_face = 1 - END IF - - Mesh => CurrentModel % Solver % Mesh - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - Ind => Element % NodeIndexes - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3) - FaceMap => GetEdgeMap(3) - - IF (.NOT. PRESENT(FaceIndex)) last_face = 3 - IF (SIZE(RevertSign) < last_face) CALL Fatal('FaceElementOrientation', & - 'Too small array for listing element faces') - - DO q=first_face,last_face - DO j=1,2 - FaceIndices(j) = Ind(FaceMap(q,j)) - END DO - IF (Parallel) THEN - DO j=1,2 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - - IF (FaceIndices(2) < FaceIndices(1)) RevertSign(q) = .TRUE. - END DO - - CASE(5) - TetraFaceMap(1,:) = (/ 2, 1, 3 /) - TetraFaceMap(2,:) = (/ 1, 2, 4 /) - TetraFaceMap(3,:) = (/ 2, 3, 4 /) - TetraFaceMap(4,:) = (/ 3, 1, 4 /) - - FaceMap => TetraFaceMap - - IF (.NOT. PRESENT(FaceIndex)) last_face = 4 - IF (SIZE(RevertSign) < last_face) CALL Fatal('FaceElementOrientation', & - 'Too small array for listing element faces') - - DO q=first_face,last_face - DO j=1,3 - FaceIndices(j) = Ind(FaceMap(q,j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - - IF ( (FaceIndices(1) < FaceIndices(2)) .AND. (FaceIndices(1) < FaceIndices(3)) ) THEN - IF (FaceIndices(3) < FaceIndices(2)) THEN - RevertSign(q) = .TRUE. - END IF - ELSE IF ( ( FaceIndices(2) < FaceIndices(1) ) .AND. ( FaceIndices(2) < FaceIndices(3) ) ) THEN - IF ( FaceIndices(1) < FaceIndices(3) ) THEN - RevertSign(q) = .TRUE. - END IF - ELSE - IF ( FaceIndices(2) < FaceIndices(1) ) THEN - RevertSign(q) = .TRUE. - END IF - END IF - END DO - - !---------------------------------------------------------------------- - ! Another way for finding sign reversions in the case of tetrahedron. - ! This code is retained here, although it was used for verification purposes... - !---------------------------------------------------------------------- - CheckSignReversions = .FALSE. - IF (CheckSignReversions) THEN - DO q=1,4 - RevertSign2(q) = .FALSE. - i = FaceMap(q,1) - j = FaceMap(q,2) - k = FaceMap(q,3) - - IF ( ( Ind(i) < Ind(j) ) .AND. ( Ind(i) < Ind(k) ) ) THEN - A = i - IF (Ind(j) < Ind(k)) THEN - B = j - C = k - ELSE - B = k - C = j - END IF - ELSE IF ( ( Ind(j) < Ind(i) ) .AND. ( Ind(j) < Ind(k) ) ) THEN - A = j - IF (Ind(i) < Ind(k)) THEN - B = i - C = k - ELSE - B = k - C = i - END IF - ELSE - A = k - IF (Ind(i) < Ind(j)) THEN - B = i - C = j - ELSE - B = j - C = i - END IF - END IF - - t1(1) = Nodes % x(B) - Nodes % x(A) - t1(2) = Nodes % y(B) - Nodes % y(A) - t1(3) = Nodes % z(B) - Nodes % z(A) - - t2(1) = Nodes % x(C) - Nodes % x(A) - t2(2) = Nodes % y(C) - Nodes % y(A) - t2(3) = Nodes % z(C) - Nodes % z(A) - - m(1:3) = CrossProduct(t1,t2) - - SELECT CASE(q) - CASE(1) - D = 4 - CASE(2) - D = 3 - CASE(3) - D = 1 - CASE(4) - D = 2 - END SELECT - - e(1) = Nodes % x(D) - Nodes % x(A) - e(2) = Nodes % y(D) - Nodes % y(A) - e(3) = Nodes % z(D) - Nodes % z(A) - - IF ( SUM(m(1:3) * e(1:3)) > 0.0d0 ) RevertSign2(q) = .TRUE. - - END DO - - IF ( ANY(RevertSign(1:4) .NEQV. RevertSign2(1:4)) ) THEN - PRINT *, 'CONFLICTING SIGN REVERSIONS SUGGESTED' - PRINT *, RevertSign(1:4) - PRINT *, RevertSign2(1:4) - STOP - END IF - END IF - - CASE DEFAULT - CALL Fatal('FaceElementOrientation', 'Unsupported element family') - END SELECT -!----------------------------------------------------------------------------------- -END SUBROUTINE FaceElementOrientation -!----------------------------------------------------------------------------------- - -!----------------------------------------------------------------------------------- -!> This subroutine produces information about how the basis functions of face (vector) -!> elements have to be reordered to conform with the global ordering convention. -!> Currently this can handle only the tetrahedron of Nedelec's second family. -!----------------------------------------------------------------------------------- -SUBROUTINE FaceElementBasisOrdering(Element, FDofMap, FaceIndex) -!----------------------------------------------------------------------------------- - IMPLICIT NONE - - TYPE(Element_t), INTENT(IN) :: Element !< A 3-D element having 2-D faces - INTEGER, INTENT(OUT) :: FDofMap(4,3) !< Face-wise information for the basis permutation - INTEGER, OPTIONAL, INTENT(IN) :: FaceIndex !< Check just one face that is specified here -!----------------------------------------------------------------------------------- - TYPE(Mesh_t), POINTER :: Mesh - LOGICAL :: Parallel - INTEGER, POINTER :: FaceMap(:,:), Ind(:) - INTEGER, TARGET :: TetraFaceMap(4,3), FaceIndices(4) - INTEGER :: j, q, first_face, last_face -!----------------------------------------------------------------------------------- - FDofMap(4,3) = 0 - - IF (PRESENT(FaceIndex)) THEN - first_face = FaceIndex - last_face = FaceIndex - ELSE - first_face = 1 - END IF - - Mesh => CurrentModel % Solver % Mesh - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - Ind => Element % NodeIndexes - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(5) - TetraFaceMap(1,:) = (/ 2, 1, 3 /) - TetraFaceMap(2,:) = (/ 1, 2, 4 /) - TetraFaceMap(3,:) = (/ 2, 3, 4 /) - TetraFaceMap(4,:) = (/ 3, 1, 4 /) - - FaceMap => TetraFaceMap - - IF (.NOT. PRESENT(FaceIndex)) last_face = 4 - - DO q=first_face,last_face - DO j=1,3 - FaceIndices(j) = Ind(FaceMap(q,j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - - IF ( ( FaceIndices(1) < FaceIndices(2) ) .AND. ( FaceIndices(1) < FaceIndices(3) ) ) THEN - FDofMap(q,1) = 1 - IF (FaceIndices(2) < FaceIndices(3)) THEN - FDofMap(q,2) = 2 - FDofMap(q,3) = 3 - ELSE - FDofMap(q,2) = 3 - FDofMap(q,3) = 2 - END IF - ELSE IF ( ( FaceIndices(2) < FaceIndices(1) ) .AND. ( FaceIndices(2) < FaceIndices(3) ) ) THEN - FDofMap(q,1) = 2 - IF (FaceIndices(1) < FaceIndices(3)) THEN - FDofMap(q,2) = 1 - FDofMap(q,3) = 3 - ELSE - FDofMap(q,2) = 3 - FDofMap(q,3) = 1 - END IF - ELSE - FDofMap(q,1) = 3 - IF (FaceIndices(1) < FaceIndices(2)) THEN - FDofMap(q,2) = 1 - FDofMap(q,3) = 2 - ELSE - FDofMap(q,2) = 2 - FDofMap(q,3) = 1 - END IF - END IF - END DO - - CASE DEFAULT - CALL Fatal('FaceElementBasisOrdering', 'Unsupported element family') - END SELECT -!----------------------------------------------------------------------------------- -END SUBROUTINE FaceElementBasisOrdering -!----------------------------------------------------------------------------------- - -!------------------------------------------------------------------------------ -!> Perform the cross product of two vectors -!------------------------------------------------------------------------------ - FUNCTION CrossProduct( v1, v2 ) RESULT( v3 ) -!------------------------------------------------------------------------------ - IMPLICIT NONE - REAL(KIND=dp) :: v1(3), v2(3), v3(3) - v3(1) = v1(2)*v2(3) - v1(3)*v2(2) - v3(2) = -v1(1)*v2(3) + v1(3)*v2(1) - v3(3) = v1(1)*v2(2) - v1(2)*v2(1) -!------------------------------------------------------------------------------ - END FUNCTION CrossProduct -!------------------------------------------------------------------------------ - - -!---------------------------------------------------------------------------------- -!> Return H(curl)-conforming edge element basis function values and their Curl -!> with respect to the reference element coordinates at a given point on the -!> reference element. Here the basis for a real element K is constructed by -!> transforming the basis functions defined on the reference element k via a version -!> of the Piola transformation designed for functions in H(curl). This construction -!> differs from the approach taken in the alternate subroutine GetEdgeBasis, which -!> does not make reference to the Piola transformation and hence may have limitations -!> in its extendability. The data for performing the Piola transformation is also returned. -!> Note that the reference element is chosen as in the p-approximation so that -!> the reference element edges/faces have the same length/area. This choice simplifies -!> the associated assembly procedure. -!> With giving the optional argument ApplyPiolaTransform = .TRUE., this function -!> also performs the Piola transform, so that the basis functions and their spatial -!> curl as defined on the physical element are returned. -!> In the lowest-order case this function returns the basis functions belonging -!> to the optimal family which is not subject to degradation of convergence on -!> meshes consisting of non-affine physical elements. The second-order elements -!> are members of the Nedelec's first family and are constructed in a hierarchic -!> fashion (the lowest-order basis functions give a partial construction of -!> the second-order basis). -!--------------------------------------------------------------------------------- - FUNCTION EdgeElementInfo( Element, Nodes, u, v, w, F, G, detF, & - Basis, EdgeBasis, RotBasis, dBasisdx, SecondFamily, BasisDegree, & - ApplyPiolaTransform, ReadyEdgeBasis, ReadyRotBasis, & - TangentialTrMapping) RESULT(stat) -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Data corresponding to the classic element nodes - REAL(KIND=dp) :: u !< 1st reference element coordinate at which the basis functions are evaluated - REAL(KIND=dp) :: v !< 2nd local coordinate - REAL(KIND=dp) :: w !< 3rd local coordinate - REAL(KIND=dp), OPTIONAL :: F(3,3) !< The gradient F=Grad f, with f the element map f:k->K - REAL(KIND=dp), OPTIONAL :: G(3,3) !< The transpose of the inverse of the gradient F - REAL(KIND=dp) :: detF !< The determinant of the gradient matrix F - REAL(KIND=dp) :: Basis(:) !< H1-conforming basis functions evaluated at (u,v,w) - REAL(KIND=dp) :: EdgeBasis(:,:) !< The basis functions b spanning the reference element space - REAL(KIND=dp), OPTIONAL :: RotBasis(:,:) !< The Curl of the edge basis functions with respect to the local coordinates - REAL(KIND=dp), OPTIONAL :: dBasisdx(:,:) !< The first derivatives of the H1-conforming basis functions at (u,v,w) - LOGICAL, OPTIONAL :: SecondFamily !< If .TRUE., a Nedelec basis of the second kind is returned (only simplicial elements) - INTEGER, OPTIONAL :: BasisDegree !< The approximation degree 2 is also supported - LOGICAL, OPTIONAL :: ApplyPiolaTransform !< If .TRUE., perform the Piola transform so that, instead of b - !< and Curl b, return B(f(p)) and (curl B)(f(p)) with B(x) the basis - !< functions on the physical element and curl the spatial curl operator. - !< In this case the absolute value of detF is returned. - REAL(KIND=dp), OPTIONAL :: ReadyEdgeBasis(:,:) !< A pretabulated edge basis function can be given - REAL(KIND=dp), OPTIONAL :: ReadyRotBasis(:,:) !< The preretabulated Curl of the edge basis function - LOGICAL, OPTIONAL :: TangentialTrMapping !< To return b x n, with n=(0,0,1) the normal to the 2D reference element. - !< The Piola transform is then the usual div-conforming version. - LOGICAL :: Stat !< .FALSE. for a degenerate element -!----------------------------------------------------------------------------------------------------------------- -! Local variables -!------------------------------------------------------------------------------------------------------------ - TYPE(Mesh_t), POINTER :: Mesh - INTEGER :: n, dim, cdim, q, i, j, k, l, ni, nj, A, I1, I2, FaceIndices(4) - REAL(KIND=dp) :: dLbasisdx(MAX(SIZE(Nodes % x),SIZE(Basis)),3), WorkBasis(4,3), WorkCurlBasis(4,3) - REAL(KIND=dp) :: D1, D2, B(3), curlB(3), GT(3,3), LG(3,3), LF(3,3) - REAL(KIND=dp) :: ElmMetric(3,3), detJ, CurlBasis(54,3) - REAL(KIND=dp) :: t(3), s(3), v1, v2, v3, h1, h2, h3, dh1, dh2, dh3, grad(2) - REAL(KIND=dp) :: LBasis(Element % TYPE % NumberOfNodes), Beta(4), EdgeSign(16) - LOGICAL :: Create2ndKindBasis, PerformPiolaTransform, UsePretabulatedBasis, Parallel - LOGICAL :: SecondOrder, ApplyTraceMapping, Found - INTEGER, POINTER :: EdgeMap(:,:), Ind(:) - INTEGER :: TriangleFaceMap(3), SquareFaceMap(4), BrickFaceMap(6,4), PrismSquareFaceMap(3,4), DOFs -!---------------------------------------------------------------------------------------------------------- - - Mesh => CurrentModel % Solver % Mesh - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - - stat = .TRUE. - Basis = 0.0d0 - EdgeBasis = 0.0d0 - WorkBasis = 0.0d0 - CurlBasis = 0.0d0 - LG = 0.0d0 - !-------------------------------------------------------------------------------------------- - ! Check whether ready edge basis function values are available to reduce computation. - ! If they are available, this function is used primarily to obtain the Piola transformation. - !-------------------------------------------------------------------------------------------- - UsePretabulatedBasis = .FALSE. - IF ( PRESENT(ReadyEdgeBasis) .AND. PRESENT(ReadyRotBasis) ) UsePretabulatedBasis = .TRUE. - !------------------------------------------------------------------------------------------ - ! Check whether the Nedelec basis functions of the second kind or higher order basis - ! functions should be created and whether the Piola transform is already applied within - ! this function. - !------------------------------------------------------------------------------------------ - Create2ndKindBasis = .FALSE. - IF ( PRESENT(SecondFamily) ) Create2ndKindBasis = SecondFamily - SecondOrder = .FALSE. - IF ( PRESENT(BasisDegree) ) THEN - SecondOrder = BasisDegree > 1 - END IF - PerformPiolaTransform = .FALSE. - IF ( PRESENT(ApplyPiolaTransform) ) PerformPiolaTransform = ApplyPiolaTransform - - ApplyTraceMapping = .FALSE. - IF ( PRESENT(TangentialTrMapping) ) ApplyTraceMapping = TangentialTrMapping - !------------------------------------------------------------------------------------------- - dLbasisdx = 0.0d0 - n = Element % TYPE % NumberOfNodes - dim = Element % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - - IF ( Element % TYPE % ElementCode == 101 ) THEN - detF = 1.0d0 - Basis(1) = 1.0d0 - IF ( PRESENT(dBasisdx) ) dBasisdx(1,:) = 0.0d0 - RETURN - END IF - - IF (cdim == 2 .AND. dim==1) THEN - CALL Warn('EdgeElementInfo', 'Traces of 2-D edge elements have not been implemented yet') - RETURN - END IF - - !----------------------------------------------------------------------- - ! The standard nodal basis functions on the reference element and - ! their derivatives with respect to the local coordinates. These define - ! the mapping of the reference element to an actual element on the background - ! mesh but are not the basis functions for the edge element approximation. - ! Remark: Using reference elements having the edges of the same length - ! simplifies the implementation of element assembly procedures. - !----------------------------------------------------------------------- - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3) - IF (SecondOrder) THEN - ! DOFs is the number of H(curl)-conforming basis functions: - DOFs = 8 - IF (n == 6) THEN - ! Here the element of the background mesh is of type 306. - ! The Lagrange interpolation basis on the p-approximation reference element: - Basis(1) = (3.0d0*u**2 + v*(-Sqrt(3.0d0) + v) + u*(-3.0d0 + 2.0d0*Sqrt(3.0d0)*v))/6.0d0 - dLBasisdx(1,1) = -0.5d0 + u + v/Sqrt(3.0d0) - dLBasisdx(1,2) = (-Sqrt(3.0d0) + 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v)/6.0d0 - Basis(2) = (3.0d0*u**2 + v*(-Sqrt(3.0d0) + v) + u*(3.0d0 - 2.0d0*Sqrt(3.0d0)*v))/6.0d0 - dLBasisdx(2,1) = 0.5d0 + u - v/Sqrt(3.d0) - dLBasisdx(2,2) = (-Sqrt(3.0d0) - 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v)/6.0d0 - Basis(3) = (v*(-Sqrt(3.0d0) + 2.0d0*v))/3.0d0 - dLBasisdx(3,1) = 0.0d0 - dLBasisdx(3,2) = -(1.0d0/Sqrt(3.0d0)) + (4.0d0*v)/3.0d0 - Basis(4) = (3.0d0 - 3.0d0*u**2 - 2.0d0*Sqrt(3.0d0)*v + v**2)/3.0d0 - dLBasisdx(4,1) = -2.0d0*u - dLBasisdx(4,2) = (-2.0d0*(Sqrt(3.0d0) - v))/3.0d0 - Basis(5) = (2.0d0*(Sqrt(3.0d0) + Sqrt(3.0d0)*u - v)*v)/3.0d0 - dLBasisdx(5,1) = (2.0d0*v)/Sqrt(3.0d0) - dLBasisdx(5,2) = (2.0d0*(Sqrt(3.0d0) + Sqrt(3.0d0)*u - 2.0d0*v))/3.0d0 - Basis(6) = (-2.0d0*v*(-Sqrt(3.0d0) + Sqrt(3.0d0)*u + v))/3.0d0 - dLBasisdx(6,1) = (-2.0d0*v)/Sqrt(3.0d0) - dLBasisdx(6,2) = (-2.0d0*(-Sqrt(3.0d0) + Sqrt(3.0d0)*u + 2.0d0*v))/3.0d0 - ELSE - ! Here the element of the background mesh is of type 303: - DO q=1,3 - Basis(q) = TriangleNodalPBasis(q, u, v) - dLBasisdx(q,1:2) = dTriangleNodalPBasis(q, u, v) - END DO - END IF - ELSE - DO q=1,n - Basis(q) = TriangleNodalPBasis(q, u, v) - dLBasisdx(q,1:2) = dTriangleNodalPBasis(q, u, v) - END DO - IF (Create2ndKindBasis) THEN - DOFs = 6 - ELSE - DOFs = 3 - END IF - END IF - CASE(4) - IF (SecondOrder) THEN - ! The second-order quad from the Nedelec's first family: affine physical elements may be needed - DOFs = 12 - ELSE - ! The lowest-order quad from the optimal family (ABF_0) - DOFs = 6 - END IF - IF (n>4) THEN - ! Here the background mesh is supposed to be of type 408/409 - CALL NodalBasisFunctions2D(Basis, Element, u, v) - CALL NodalFirstDerivatives(n, dLBasisdx, Element, u, v, w) - ELSE - ! Here the background mesh is of type 404 - DO q=1,4 - Basis(q) = QuadNodalPBasis(q, u, v) - dLBasisdx(q,1:2) = dQuadNodalPBasis(q, u, v) - END DO - END IF - CASE(5) - IF (SecondOrder) THEN - DOFs = 20 - IF (n == 10) THEN - ! Here the element of the background mesh is of type 510. - ! The Lagrange interpolation basis on the p-approximation reference element: - Basis(1) = (6.0d0*u**2 - 2.0d0*Sqrt(3.0d0)*v + 2.0d0*v**2 - Sqrt(6.0d0)*w + 2.0d0*Sqrt(2.0d0)*v*w + & - w**2 + 2.0d0*u*(-3.0d0 + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/12.0d0 - dLBasisdx(1,1) = -0.5d0 + u + v/Sqrt(3.0d0) + w/Sqrt(6.0d0) - dLBasisdx(1,2) = (-Sqrt(3.0d0) + 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v + Sqrt(2.0d0)*w)/6.0d0 - dLBasisdx(1,3) = (-Sqrt(6.0d0) + 2.0d0*Sqrt(6.0d0)*u + 2.0d0*Sqrt(2.0d0)*v + 2.0d0*w)/12.0d0 - Basis(2) = (6.0d0*u**2 - 2.0d0*Sqrt(3.0d0)*v + 2.0d0*v**2 - Sqrt(6.0d0)*w + 2.0d0*Sqrt(2.0d0)*v*w + & - w**2 - 2.0d0*u*(-3.0d0 + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/12.0d0 - dLBasisdx(2,1) = 0.5d0 + u - v/Sqrt(3.0d0) - w/Sqrt(6.0d0) - dLBasisdx(2,2) = (-Sqrt(3.0d0) - 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v + Sqrt(2.0d0)*w)/6.0d0 - dLBasisdx(2,3) = (-Sqrt(6.0d0) - 2.0d0*Sqrt(6.0d0)*u + 2.0d0*Sqrt(2.0d0)*v + 2.0d0*w)/12.0d0 - Basis(3) = (8.0d0*v**2 + w*(Sqrt(6.0d0) + w) - 4.0d0*v*(Sqrt(3.0d0) + Sqrt(2.0d0)*w))/12.0d0 - dLBasisdx(3,1) = 0.0d0 - dLBasisdx(3,2) = (-Sqrt(3.0d0) + 4.0d0*v - Sqrt(2.0d0)*w)/3.0d0 - dLBasisdx(3,3) = (Sqrt(6.0d0) - 4.0d0*Sqrt(2.0d0)*v + 2.0d0*w)/12.0d0 - Basis(4) = (w*(-Sqrt(6.0d0) + 3.0d0*w))/4.0d0 - dLBasisdx(4,1) = 0.0d0 - dLBasisdx(4,2) = 0.0d0 - dLBasisdx(4,3) = (-Sqrt(6.0d0) + 6.0d0*w)/4.0d0 - Basis(5) = (6.0d0 - 6.0d0*u**2 - 4.0d0*Sqrt(3.0d0)*v + 2.0d0*v**2 - 2.0d0*Sqrt(6.0d0)*w + & - 2.0d0*Sqrt(2.0d0)*v*w + w**2)/6.0d0 - dLBasisdx(5,1) = -2.0d0*u - dLBasisdx(5,2) = (-2.0d0*Sqrt(3.0d0) + 2.0d0*v + Sqrt(2.0d0)*w)/3.0d0 - dLBasisdx(5,3) = (-Sqrt(6.0d0) + Sqrt(2.0d0)*v + w)/3.0d0 - Basis(6) = (-4.0d0*v**2 + w*(-Sqrt(6.0d0) - Sqrt(6.0d0)*u + w) + v*(4.0d0*Sqrt(3.0d0) + & - 4.0d0*Sqrt(3.0d0)*u - Sqrt(2.0d0)*w))/6.0d0 - dLBasisdx(6,1) = (2.0d0*v)/Sqrt(3.0d0) - w/Sqrt(6.0d0) - dLBasisdx(6,2) = (4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u - 8.0d0*v - Sqrt(2.0d0)*w)/6.0d0 - dLBasisdx(6,3) = (-Sqrt(6.0d0) - Sqrt(6.0d0)*u - Sqrt(2.0d0)*v + 2.0d0*w)/6.0d0 - Basis(7) = (-4.0d0*v**2 + w*(-Sqrt(6.0d0) + Sqrt(6.0d0)*u + w) - & - v*(-4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u + Sqrt(2.0d0)*w))/6.0d0 - dLBasisdx(7,1) = (-2.0d0*v)/Sqrt(3.0d0) + w/Sqrt(6.0d0) - dLBasisdx(7,2) = (4.0d0*Sqrt(3.0d0) - 4.0d0*Sqrt(3.0d0)*u - 8.0d0*v - Sqrt(2.0d0)*w)/6.0d0 - dLBasisdx(7,3) = (-Sqrt(6.0d0) + Sqrt(6.0d0)*u - Sqrt(2.0d0)*v + 2.0d0*w)/6.0d0 - Basis(8) = -(w*(-Sqrt(6.0d0) + Sqrt(6.0d0)*u + Sqrt(2.0d0)*v + w))/2.0d0 - dLBasisdx(8,1) = -(Sqrt(1.5d0)*w) - dLBasisdx(8,2) = -(w/Sqrt(2.0d0)) - dLBasisdx(8,3) = (Sqrt(6.0d0) - Sqrt(6.0d0)*u - Sqrt(2.0d0)*v - 2.0d0*w)/2.0d0 - Basis(9) = ((Sqrt(6.0d0) + Sqrt(6.0d0)*u - Sqrt(2.0d0)*v - w)*w)/2.0d0 - dLBasisdx(9,1) = Sqrt(1.5d0)*w - dLBasisdx(9,2) = -(w/Sqrt(2.0d0)) - dLBasisdx(9,3) = (Sqrt(6.0d0) + Sqrt(6.0d0)*u - Sqrt(2.0d0)*v - 2.0d0*w)/2.0d0 - Basis(10) = Sqrt(2.0d0)*v*w - w**2/2.0d0 - dLBasisdx(10,1) = 0.0d0 - dLBasisdx(10,2) = Sqrt(2.0d0)*w - dLBasisdx(10,3) = Sqrt(2.0d0)*v - w - ELSE - ! Here the element of the background mesh is of type 504: - DO q=1,4 - Basis(q) = TetraNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dTetraNodalPBasis(q, u, v, w) - END DO - END IF - ELSE - DO q=1,n - Basis(q) = TetraNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dTetraNodalPBasis(q, u, v, w) - END DO - IF (Create2ndKindBasis) THEN - DOFs = 12 - ELSE - DOFs = 6 - END IF - END IF - CASE(6) - IF (SecondOrder) THEN - ! The second-order pyramid from the Nedelec's first family - DOFs = 31 - ELSE - ! The lowest-order pyramid from the optimal family - DOFs = 10 - END IF - - IF (n==13) THEN - ! Here the background mesh is supposed to be of type 613. The difference between the standard - ! reference element and the p-reference element can be taken into account by a simple scaling: - CALL NodalBasisFunctions3D(Basis, Element, u, v, sqrt(2.0d0)*w) - CALL NodalFirstDerivatives(n, dLBasisdx, Element, u, v, sqrt(2.0d0)*w) - dLBasisdx(1:n,3) = sqrt(2.0d0) * dLBasisdx(1:n,3) - ELSE - ! Background mesh elements of the type 605: - DO q=1,n - Basis(q) = PyramidNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dPyramidNodalPBasis(q, u, v, w) - END DO - END IF - - CASE(7) - IF (SecondOrder) THEN - ! The second-order prism from the Nedelec's first family: affine physical elements may be needed - DOFs = 36 - ELSE - ! The lowest-order prism from the optimal family - DOFs = 15 - END IF - - IF (n==15) THEN - ! Here the background mesh is of type 715. - ! The Lagrange interpolation basis on the p-approximation reference element: - - h1 = -0.5d0*w + 0.5d0*w**2 - h2 = 0.5d0*w + 0.5d0*w**2 - h3 = 1.0d0 - w**2 - dh1 = -0.5d0 + w - dh2 = 0.5d0 + w - dh3 = -2.0d0 * w - - WorkBasis(1,1) = (3.0d0*u**2 + v*(-Sqrt(3.0d0) + v) + u*(-3.0d0 + 2.0d0*Sqrt(3.0d0)*v))/6 - grad(1) = -0.5d0 + u + v/Sqrt(3.0d0) - grad(2) = (-Sqrt(3.0d0) + 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v)/6.0d0 - Basis(1) = WorkBasis(1,1) * h1 - dLBasisdx(1,1:2) = grad(1:2) * h1 - dLBasisdx(1,3) = WorkBasis(1,1) * dh1 - Basis(4) = WorkBasis(1,1) * h2 - dLBasisdx(4,1:2) = grad(1:2) * h2 - dLBasisdx(4,3) = WorkBasis(1,1) * dh2 - Basis(13) = WorkBasis(1,1) * h3 - dLBasisdx(13,1:2) = grad(1:2) * h3 - dLBasisdx(13,3) = WorkBasis(1,1) * dh3 - - WorkBasis(1,1) = (3.0d0*u**2 + v*(-Sqrt(3.0d0) + v) + u*(3.0d0 - 2.0d0*Sqrt(3.0d0)*v))/6.0d0 - grad(1) = 0.5d0 + u - v/Sqrt(3.d0) - grad(2) = (-Sqrt(3.0d0) - 2.0d0*Sqrt(3.0d0)*u + 2.0d0*v)/6.0d0 - Basis(2) = WorkBasis(1,1) * h1 - dLBasisdx(2,1:2) = grad(1:2) * h1 - dLBasisdx(2,3) = WorkBasis(1,1) * dh1 - Basis(5) = WorkBasis(1,1) * h2 - dLBasisdx(5,1:2) = grad(1:2) * h2 - dLBasisdx(5,3) = WorkBasis(1,1) * dh2 - Basis(14) = WorkBasis(1,1) * h3 - dLBasisdx(14,1:2) = grad(1:2) * h3 - dLBasisdx(14,3) = WorkBasis(1,1) * dh3 - - WorkBasis(1,1) = (v*(-Sqrt(3.0d0) + 2.0d0*v))/3.0d0 - grad(1) = 0.0d0 - grad(2) = -(1.0d0/Sqrt(3.0d0)) + (4.0d0*v)/3.0d0 - Basis(3) = WorkBasis(1,1) * h1 - dLBasisdx(3,1:2) = grad(1:2) * h1 - dLBasisdx(3,3) = WorkBasis(1,1) * dh1 - Basis(6) = WorkBasis(1,1) * h2 - dLBasisdx(6,1:2) = grad(1:2) * h2 - dLBasisdx(6,3) = WorkBasis(1,1) * dh2 - Basis(15) = WorkBasis(1,1) * h3 - dLBasisdx(15,1:2) = grad(1:2) * h3 - dLBasisdx(15,3) = WorkBasis(1,1) * dh3 - - h1 = 0.5d0 * (1.0d0 - w) - dh1 = -0.5d0 - h2 = 0.5d0 * (1.0d0 + w) - dh2 = 0.5d0 - - WorkBasis(1,1) = (3.0d0 - 3.0d0*u**2 - 2.0d0*Sqrt(3.0d0)*v + v**2)/3.0d0 - grad(1) = -2.0d0*u - grad(2) = (-2.0d0*(Sqrt(3.0d0) - v))/3.0d0 - Basis(7) = WorkBasis(1,1) * h1 - dLBasisdx(7,1:2) = grad(1:2) * h1 - dLBasisdx(7,3) = WorkBasis(1,1) * dh1 - Basis(10) = WorkBasis(1,1) * h2 - dLBasisdx(10,1:2) = grad(1:2) * h2 - dLBasisdx(10,3) = WorkBasis(1,1) * dh2 - - WorkBasis(1,1) = (2.0d0*(Sqrt(3.0d0) + Sqrt(3.0d0)*u - v)*v)/3.0d0 - grad(1) = (2.0d0*v)/Sqrt(3.0d0) - grad(2) = (2.0d0*(Sqrt(3.0d0) + Sqrt(3.0d0)*u - 2.0d0*v))/3.0d0 - Basis(8) = WorkBasis(1,1) * h1 - dLBasisdx(8,1:2) = grad(1:2) * h1 - dLBasisdx(8,3) = WorkBasis(1,1) * dh1 - Basis(11) = WorkBasis(1,1) * h2 - dLBasisdx(11,1:2) = grad(1:2) * h2 - dLBasisdx(11,3) = WorkBasis(1,1) * dh2 - - WorkBasis(1,1) = (-2.0d0*v*(-Sqrt(3.0d0) + Sqrt(3.0d0)*u + v))/3.0d0 - grad(1) = (-2.0d0*v)/Sqrt(3.0d0) - grad(2) = (-2.0d0*(-Sqrt(3.0d0) + Sqrt(3.0d0)*u + 2.0d0*v))/3.0d0 - Basis(9) = WorkBasis(1,1) * h1 - dLBasisdx(9,1:2) = grad(1:2) * h1 - dLBasisdx(9,3) = WorkBasis(1,1) * dh1 - Basis(12) = WorkBasis(1,1) * h2 - dLBasisdx(12,1:2) = grad(1:2) * h2 - dLBasisdx(12,3) = WorkBasis(1,1) * dh2 - ELSE - ! Here the background mesh is of type 706 - DO q=1,n - Basis(q) = WedgeNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dWedgeNodalPBasis(q, u, v, w) - END DO - END IF - CASE(8) - IF (SecondOrder) THEN - ! The second-order brick from the Nedelec's first family: affine physical elements may be needed - DOFs = 54 - ELSE - ! The lowest-order brick from the optimal family - DOFs = 27 - END IF - IF (n>8) THEN - ! Here the background mesh is supposed to be of type 820/827 - CALL NodalBasisFunctions3D(Basis, Element, u, v, w) - CALL NodalFirstDerivatives(n, dLBasisdx, Element, u, v, w) - ELSE - ! Here the background mesh is of type 808 - DO q=1,n - Basis(q) = BrickNodalPBasis(q, u, v, w) - dLBasisdx(q,1:3) = dBrickNodalPBasis(q, u, v, w) - END DO - END IF - CASE DEFAULT - CALL Fatal('ElementDescription::EdgeElementInfo','Unsupported element type') - END SELECT - - !----------------------------------------------------------------------- - ! Get data for performing the Piola transformation... - !----------------------------------------------------------------------- - stat = PiolaTransformationData(n, Element, Nodes, LF, detF, dLBasisdx) - !------------------------------------------------------------------------ - ! ... in order to define the basis for the element space X(K) via - ! applying a version of the Piola transformation as - ! X(K) = { B | B = F^{-T}(f^{-1}(x)) b(f^{-1}(x)) } - ! with b giving the edge basis function on the reference element k, - ! f mapping k to the actual element K, i.e. K = f(k) and F = Grad f. This - ! function returns the local basis functions b and their Curl (with respect - ! to local coordinates) evaluated at the integration point. The effect of - ! the Piola transformation need to be considered when integrating, so we - ! shall return also the values of F, G=F^{-T} and det F. - ! - ! The construction of edge element bases could be done in an alternate way for - ! triangles and tetrahedra, while the chosen approach has the benefit that - ! it generalizes to other cases. For example general quadrilaterals may now - ! be handled in the same way. - !--------------------------------------------------------------------------- - IF (cdim == dim) THEN - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3,4) - LG(1,1) = 1.0d0/detF * LF(2,2) - LG(1,2) = -1.0d0/detF * LF(1,2) - LG(2,1) = -1.0d0/detF * LF(2,1) - LG(2,2) = 1.0d0/detF * LF(1,1) - CASE(5,6,7,8) - CALL InvertMatrix3x3(LF,LG,detF) - CASE DEFAULT - CALL Fatal('ElementDescription::EdgeElementInfo','Unsupported element type') - END SELECT - LG(1:dim,1:dim) = TRANSPOSE( LG(1:dim,1:dim) ) - END IF - - IF (UsePretabulatedBasis) THEN - DO i=1,DOFs - EdgeBasis(i,1:3) = ReadyEdgeBasis(i,1:3) - CurlBasis(i,1:3) = ReadyRotBasis(i,1:3) - END DO - ELSE - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3) - !-------------------------------------------------------------- - ! This branch is for handling triangles. Note that - ! the global orientation of the edge tangent t is defined such that - ! t points towards the node that has a larger global index. - !-------------------------------------------------------------- - EdgeMap => GetEdgeMap(3) - !EdgeMap => GetEdgeMap(GetElementFamily(Element)) - - IF (Create2ndKindBasis) THEN - !------------------------------------------------- - ! Two basis functions defined on the edge 12. - !------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj Element % Nodeindexes - - DO j=1,3 - FaceIndices(j) = Ind(TriangleFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL TriangleFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - WorkBasis(1,1) = ((Sqrt(3.0d0) - v)*v)/6.0d0 - WorkBasis(1,2) = (u*v)/6.0d0 - WorkCurlBasis(1,3) = (-Sqrt(3.0d0) + 3.0d0*v)/6.0d0 - WorkBasis(2,1) = (v*(1.0d0 + u - v/Sqrt(3.0d0)))/(4.0d0*Sqrt(3.0d0)) - WorkBasis(2,2) = ((-1.0d0 + u)*(-3.0d0 - 3.0d0*u + Sqrt(3.0d0)*v))/(12.0d0*Sqrt(3.0d0)) - WorkCurlBasis(2,3) =(-Sqrt(3.0d0) - 3.0d0*Sqrt(3.0d0)*u + 3.0d0*v)/12.0d0 - WorkBasis(3,1) = (v*(-3.0d0 + 3.0d0*u + Sqrt(3.0d0)*v))/(12.0d0*Sqrt(3.0d0)) - WorkBasis(3,2) = -((1.0d0 + u)*(-3.0d0 + 3.0d0*u + Sqrt(3.0d0)*v))/(12.0d0*Sqrt(3.0d0)) - WorkCurlBasis(3,3) = (Sqrt(3.0d0) - 3.0d0*Sqrt(3.0d0)*u - 3.0d0*v)/12.0d0 - - EdgeBasis(7,:) = D1 * WorkBasis(I1,:) - CurlBasis(7,3) = D1 * WorkCurlBasis(I1,3) - EdgeBasis(8,:) = D2 * WorkBasis(I2,:) - CurlBasis(8,3) = D2 * WorkCurlBasis(I2,3) - - END IF - END IF - - CASE(4) - !-------------------------------------------------------------- - ! This branch is for handling quadrilaterals - !-------------------------------------------------------------- - EdgeMap => GetEdgeMap(4) - IF (SecondOrder) THEN - !--------------------------------------------------------------- - ! The second-order element from the Nedelec's first family with - ! a hierarchic basis. This element may not be optimally accurate - ! if the physical element is not affine. - ! First, the eight basis functions associated with the edges: - !-------------------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - EdgeBasis(1,1) = 0.1D1 / 0.4D1 - v / 0.4D1 - CurlBasis(1,3) = 0.1D1 / 0.4D1 - IF (nj Element % Nodeindexes - - WorkBasis = 0.0d0 - WorkCurlBasis = 0.0d0 - - WorkBasis(1,1) = 0.2D1 * (0.1D1 / 0.2D1 - v / 0.2D1) * (0.1D1 / 0.2D1 + v / 0.2D1) - WorkCurlBasis(1,3) = v - WorkBasis(2,1) = 0.12D2 * u * (0.1D1 / 0.2D1 - v / 0.2D1) * (0.1D1 / 0.2D1 + v / 0.2D1) - WorkCurlBasis(2,3) = 0.6D1 * u * (0.1D1 / 0.2D1 + v / 0.2D1) - & - 0.6D1 * u * (0.1D1 / 0.2D1 - v / 0.2D1) - - WorkBasis(3,2) = 0.2D1 * (0.1D1 / 0.2D1 - u / 0.2D1) * (0.1D1 / 0.2D1 + u / 0.2D1) - WorkCurlBasis(3,3) = -u - WorkBasis(4,2) = 0.12D2 * v * (0.1D1 / 0.2D1 - u / 0.2D1) * (0.1D1 / 0.2D1 + u / 0.2D1) - WorkCurlBasis(4,3) = -0.6D1 * v * (0.1D1 / 0.2D1 + u / 0.2D1) + & - 0.6D1 * v * (0.1D1 / 0.2D1 - u / 0.2D1) - - DO j=1,4 - FaceIndices(j) = Ind(SquareFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,4 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - EdgeBasis(9,:) = D1 * WorkBasis(2*(I1-1)+1,:) - CurlBasis(9,:) = D1 * WorkCurlBasis(2*(I1-1)+1,:) - EdgeBasis(10,:) = WorkBasis(2*(I1-1)+2,:) - CurlBasis(10,:) = WorkCurlBasis(2*(I1-1)+2,:) - EdgeBasis(11,:) = D2 * WorkBasis(2*(I2-1)+1,:) - CurlBasis(11,:) = D2 * WorkCurlBasis(2*(I2-1)+1,:) - EdgeBasis(12,:) = WorkBasis(2*(I2-1)+2,:) - CurlBasis(12,:) = WorkCurlBasis(2*(I2-1)+2,:) - - ELSE - !------------------------------------------------------ - ! The Arnold-Boffi-Falk element of degree k=0 which is - ! a member of the optimal edge element family. - ! First, four basis functions defined on the edges - !------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - EdgeBasis(1,1) = ((-1.0d0 + v)*v)/4.0d0 - EdgeBasis(1,2) = 0.0d0 - CurlBasis(1,3) = (1.0d0 - 2*v)/4.0d0 - IF (nj Element % Nodeindexes - - WorkBasis(1,:) = 0.0d0 - WorkBasis(2,:) = 0.0d0 - WorkCurlBasis(1,:) = 0.0d0 - WorkCurlBasis(2,:) = 0.0d0 - - WorkBasis(1,1) = (1.0d0 - v**2)/2.0d0 - WorkBasis(1,2) = 0.0d0 - WorkCurlBasis(1,3) = v - - WorkBasis(2,1) = 0.0d0 - WorkBasis(2,2) = (1.0d0 - u**2)/2.0d0 - WorkCurlBasis(2,3) = -u - - DO j=1,4 - FaceIndices(j) = Ind(SquareFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,4 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - EdgeBasis(5,:) = D1 * WorkBasis(I1,:) - CurlBasis(5,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(6,:) = D2 * WorkBasis(I2,:) - CurlBasis(6,:) = D2 * WorkCurlBasis(I2,:) - END IF - - CASE(5) - !-------------------------------------------------------------- - ! This branch is for handling tetrahedra - !-------------------------------------------------------------- - EdgeMap => GetEdgeMap(5) - - IF (Create2ndKindBasis) THEN - !------------------------------------------------- - ! Two basis functions defined on the edge 12. - !------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj Element % Nodeindexes - - DO j=1,3 - FaceIndices(j) = Ind(TriangleFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL TriangleFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - WorkBasis(1,1) = ((4.0d0*v - Sqrt(2.0d0)*w)*& - (-6.0d0 + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(48.0d0*Sqrt(3.0d0)) - WorkBasis(1,2) = -(u*(4.0d0*v - Sqrt(2.0d0)*w))/24.0d0 - WorkBasis(1,3) = (u*(-2.0d0*Sqrt(2.0d0)*v + w))/24.0d0 - WorkCurlBasis(1,1) = -u/(4.0d0*Sqrt(2.0d0)) - WorkCurlBasis(1,2) = (Sqrt(6.0d0) + 3.0d0*Sqrt(2.0d0)*v - 3.0d0*w)/24.0d0 - WorkCurlBasis(1,3) = (Sqrt(3.0d0) - 3.0d0*v)/6.0d0 - - WorkBasis(2,1) = ((4.0d0*v - Sqrt(2.0d0)*w)*(-6.0d0 + 6.0d0*u + & - 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(96.0d0*Sqrt(3.0d0)) - WorkBasis(2,2) = -((4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u - 3.0d0*Sqrt(2.0d0)*w)*& - (-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/288.0d0 - WorkBasis(2,3) = ((Sqrt(3.0d0) + Sqrt(3.0d0)*u - 3.0d0*v)*& - (-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(144.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,1) = -(-6.0d0 + 2.0d0*u + 2.0d0*Sqrt(3.0d0)*v + & - Sqrt(6.0d0)*w)/(16.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,2) = (2.0d0*Sqrt(3.0d0) - 6.0d0*Sqrt(3.0d0)*u + & - 6.0d0*v - 3.0d0*Sqrt(2.0d0)*w)/(48.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,3) = (Sqrt(3.0d0) - 3.0d0*Sqrt(3.0d0)*u - 3.0d0*v)/12.0d0 - - WorkBasis(3,1) = -((4.0d0*v - Sqrt(2.0d0)*w)*(-6.0d0 - 6.0d0*u + & - 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(96.0d0*Sqrt(3.0d0)) - WorkBasis(3,2) = ((-4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u + 3.0d0*Sqrt(2.0d0)*w)* & - (-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/288.0d0 - WorkBasis(3,3) = -((-Sqrt(3.0d0) + Sqrt(3.0d0)*u + 3.0d0*v)* & - (-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(144.0d0*Sqrt(2.0d0)) - WorkCurlBasis(3,1) = -(-6.0d0 - 2.0d0*u + 2.0d0*Sqrt(3.0d0)*v + & - Sqrt(6.0d0)*w)/(16.0d0*Sqrt(2.0d0)) - WorkCurlBasis(3,2) = (-2.0d0*Sqrt(3.0d0) - 6.0d0*Sqrt(3.0d0)*u - 6.0d0*v + & - 3.0d0*Sqrt(2.0d0)*w)/(48.0d0*Sqrt(2.0d0)) - WorkCurlBasis(3,3) = (-Sqrt(3.0d0) - 3.0d0*Sqrt(3.0d0)*u + 3.0d0*v)/12.0d0 - - EdgeBasis(13,:) = D1 * WorkBasis(I1,:) - CurlBasis(13,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(14,:) = D2 * WorkBasis(I2,:) - CurlBasis(14,:) = D2 * WorkCurlBasis(I2,:) - - !------------------------------------------------- - ! Two basis functions defined on the face 124: - !------------------------------------------------- - TriangleFaceMap(:) = (/ 1,2,4 /) - Ind => Element % Nodeindexes - - DO j=1,3 - FaceIndices(j) = Ind(TriangleFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL TriangleFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - WorkBasis(1,1) = -(w*(-6.0d0 + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(8.0d0*Sqrt(6.0d0)) - WorkBasis(1,2) = (u*w)/(4.0d0*Sqrt(2.0d0)) - WorkBasis(1,3) = (u*w)/8.0d0 - WorkCurlBasis(1,1) = -u/(4.0d0*Sqrt(2.0d0)) - WorkCurlBasis(1,2) = (Sqrt(6.0d0) - Sqrt(2.0d0)*v - 3.0d0*w)/8.0d0 - WorkCurlBasis(1,3) = w/(2.0d0*Sqrt(2.0d0)) - - WorkBasis(2,1) = -(w*(-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + & - Sqrt(6.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(2,2) = (w*(1.0d0 + u - v/Sqrt(3.0d0) - w/Sqrt(6.0d0)))/(8.0d0*Sqrt(2.0d0)) - WorkBasis(2,3) = ((-Sqrt(3.0d0) + Sqrt(3.0d0)*u + v)* & - (-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(48.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,1) = (-3.0d0*Sqrt(2.0d0) - Sqrt(2.0d0)*u + Sqrt(6.0d0)*v + Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(2,2) = (Sqrt(6.0d0) + 3.0d0*Sqrt(6.0d0)*u - Sqrt(2.0d0)*v - 3.0d0*w)/16.0d0 - WorkCurlBasis(2,3) = w/(4.0d0*Sqrt(2.0d0)) - - WorkBasis(3,1) = (w*(-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(3,2) = -(w*(-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(48.0d0*Sqrt(2.0d0)) - WorkBasis(3,3) = -((Sqrt(6.0d0) + Sqrt(6.0d0)*u - Sqrt(2.0d0)*v)*& - (-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/96.0d0 - WorkCurlBasis(3,1) = (-3.0d0*Sqrt(2.0d0) + Sqrt(2.0d0)*u + Sqrt(6.0d0)*v + Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(3,2) = (-Sqrt(6.0d0) + 3.0d0*Sqrt(6.0d0)*u + Sqrt(2.0d0)*v + 3.0d0*w)/16.0d0 - WorkCurlBasis(3,3) = -w/(4.0d0*Sqrt(2.0d0)) - - EdgeBasis(15,:) = D1 * WorkBasis(I1,:) - CurlBasis(15,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(16,:) = D2 * WorkBasis(I2,:) - CurlBasis(16,:) = D2 * WorkCurlBasis(I2,:) - - !------------------------------------------------- - ! Two basis functions defined on the face 234: - !------------------------------------------------- - TriangleFaceMap(:) = (/ 2,3,4 /) - Ind => Element % Nodeindexes - - DO j=1,3 - FaceIndices(j) = Ind(TriangleFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL TriangleFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - WorkBasis(1,1) = (w*(-2.0d0*Sqrt(2.0d0)*v + w))/16.0d0 - WorkBasis(1,2) = (w*(4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u - & - 3.0d0*Sqrt(2.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(1,3) = -((1.0d0 + u - Sqrt(3.0d0)*v)*w)/16.0d0 - WorkCurlBasis(1,1) = (-2.0d0*Sqrt(2.0d0) - 2.0d0*Sqrt(2.0d0)*u + 3.0d0*Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(1,2) = (-2.0d0*Sqrt(2.0d0)*v + 3.0d0*w)/16.0d0 - WorkCurlBasis(1,3) = w/(2.0d0*Sqrt(2.0d0)) - - WorkBasis(2,1) = (w*(-2.0d0*Sqrt(2.0d0)*v + w))/16.0d0 - WorkBasis(2,2) = -(w*(-4.0d0*v + Sqrt(2.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(2,3) = -((Sqrt(6.0d0) + Sqrt(6.0d0)*u - Sqrt(2.0d0)*v)*& - (-4.0d0*v + Sqrt(2.0d0)*w))/(32.0d0*Sqrt(3.0d0)) - WorkCurlBasis(2,1) = (2.0d0*Sqrt(2.0d0) + 2.0d0*Sqrt(2.0d0)*u - & - 2.0d0*Sqrt(6.0d0)*v + Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(2,2) = (-4.0d0*Sqrt(2.0d0)*v + 3.0d0*w)/16.0d0 - WorkCurlBasis(2,3) = w/(4.0d0*Sqrt(2.0d0)) - - WorkBasis(3,1) = 0.0d0 - WorkBasis(3,2) = (w*(-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(24.0d0*Sqrt(2.0d0)) - WorkBasis(3,3) = -(v*(-6.0d0 - 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(24.0d0*Sqrt(2.0d0)) - WorkCurlBasis(3,1) = (2.0d0*Sqrt(2.0d0) + 2.0d0*Sqrt(2.0d0)*u - Sqrt(6.0d0)*v - Sqrt(3.0d0)*w)/8.0d0 - WorkCurlBasis(3,2) = -v/(4.0d0*Sqrt(2.0d0)) - WorkCurlBasis(3,3) = -w/(4.0d0*Sqrt(2.0d0)) - - EdgeBasis(17,:) = D1 * WorkBasis(I1,:) - CurlBasis(17,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(18,:) = D2 * WorkBasis(I2,:) - CurlBasis(18,:) = D2 * WorkCurlBasis(I2,:) - - !------------------------------------------------- - ! Two basis functions defined on the face 314: - !------------------------------------------------- - TriangleFaceMap(:) = (/ 3,1,4 /) - Ind => Element % Nodeindexes - - DO j=1,3 - FaceIndices(j) = Ind(TriangleFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,3 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL TriangleFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - WorkBasis(1,1) = (w*(-2.0d0*Sqrt(2.0d0)*v + w))/16.0d0 - WorkBasis(1,2) = (w*(-4.0d0*Sqrt(3.0d0) + 4.0d0*Sqrt(3.0d0)*u + & - 3.0d0*Sqrt(2.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(1,3) = -((-1.0d0 + u + Sqrt(3.0d0)*v)*w)/16.0d0 - WorkCurlBasis(1,1) = (2.0d0*Sqrt(2.0d0) - 2.0d0*Sqrt(2.0d0)*u - 3.0d0*Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(1,2) = (-2.0d0*Sqrt(2.0d0)*v + 3.0d0*w)/16.0d0 - WorkCurlBasis(1,3) = w/(2.0d0*Sqrt(2.0d0)) - - WorkBasis(2,1) = 0.0d0 - WorkBasis(2,2) = (w*(-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(24.0d0*Sqrt(2.0d0)) - WorkBasis(2,3) = -(v*(-6.0d0 + 6.0d0*u + 2.0d0*Sqrt(3.0d0)*v + Sqrt(6.0d0)*w))/(24.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,1) = (2.0d0*Sqrt(2.0d0) - 2.0d0*Sqrt(2.0d0)*u - Sqrt(6.0d0)*v - Sqrt(3.0d0)*w)/8.0d0 - WorkCurlBasis(2,2) = v/(4.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,3) = w/(4.0d0*Sqrt(2.0d0)) - - WorkBasis(3,1) = ((2.0d0*Sqrt(2.0d0)*v - w)*w)/16.0d0 - WorkBasis(3,2) = -(w*(-4.0d0*v + Sqrt(2.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkBasis(3,3) = ((-Sqrt(3.0d0) + Sqrt(3.0d0)*u + v)*& - (-4.0d0*v + Sqrt(2.0d0)*w))/(16.0d0*Sqrt(6.0d0)) - WorkCurlBasis(3,1) = (2.0d0*Sqrt(2.0d0) - 2.0d0*Sqrt(2.0d0)*u - & - 2.0d0*Sqrt(6.0d0)*v + Sqrt(3.0d0)*w)/16.0d0 - WorkCurlBasis(3,2) = (4.0d0*Sqrt(2.0d0)*v - 3.0d0*w)/16.0d0 - WorkCurlBasis(3,3) = -w/(4.0d0*Sqrt(2.0d0)) - - EdgeBasis(19,:) = D1 * WorkBasis(I1,:) - CurlBasis(19,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(20,:) = D2 * WorkBasis(I2,:) - CurlBasis(20,:) = D2 * WorkCurlBasis(I2,:) - END IF - END IF - - CASE(6) - !-------------------------------------------------------------- - ! This branch is for handling pyramidic elements - !-------------------------------------------------------------- - EdgeMap => GetEdgeMap(6) - Ind => Element % Nodeindexes - - IF (SecondOrder) THEN - EdgeSign = 1.0d0 - - LBasis(1) = 0.1D1 / 0.4D1 - u / 0.4D1 - v / 0.4D1 - w * sqrt(0.2D1) / 0.8D1 + & - u * v / ( (0.1D1 - w * sqrt(0.2D1) / 0.2D1) * 0.4D1 ) - LBasis(2) = 0.1D1 / 0.4D1 + u / 0.4D1 - v / 0.4D1 - w * sqrt(0.2D1) / 0.8D1 - & - u * v / ( (0.1D1 - w * sqrt(0.2D1) / 0.2D1) * 0.4D1 ) - LBasis(3) = 0.1D1 / 0.4D1 + u / 0.4D1 + v / 0.4D1 - w * sqrt(0.2D1) / 0.8D1 + & - u * v / ( (0.1D1 - w * sqrt(0.2D1) / 0.2D1) * 0.4D1 ) - LBasis(4) = 0.1D1 / 0.4D1 - u / 0.4D1 + v / 0.4D1 - w * sqrt(0.2D1) / 0.8D1 - & - u * v / ( (0.1D1 - w * sqrt(0.2D1) / 0.2D1) * 0.4D1 ) - LBasis(5) = w * sqrt(0.2D1) / 0.2D1 - - Beta(1) = 0.1D1 / 0.2D1 - u / 0.2D1 - w * sqrt(0.2D1) / 0.4D1 - Beta(2) = 0.1D1 / 0.2D1 - v / 0.2D1 - w * sqrt(0.2D1) / 0.4D1 - Beta(3) = 0.1D1 / 0.2D1 + u / 0.2D1 - w * sqrt(0.2D1) / 0.4D1 - Beta(4) = 0.1D1 / 0.2D1 + v / 0.2D1 - w * sqrt(0.2D1) / 0.4D1 - - ! Edge 12: - !-------------------------------------------------------------- - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - EdgeBasis(1,1) = 0.1D1 / 0.4D1 - v / 0.4D1 - w * sqrt(0.2D1) / 0.8D1 - EdgeBasis(1,2) = 0.0d0 - EdgeBasis(1,3) = sqrt(0.2D1) * u * (w * sqrt(0.2D1) + 2.0D0 * v - 0.2D1) / & - ((w * sqrt(0.2D1) - 0.2D1) * 0.8D1) - CurlBasis(1,1) = sqrt(0.2D1) * u / ((w * sqrt(0.2D1) - 0.2D1) * 0.4D1) - CurlBasis(1,2) = -sqrt(0.2D1) / 0.8D1 - sqrt(0.2D1) * (w * sqrt(0.2D1) + 2.0D0 * v - 0.2D1) / & - ( (w * sqrt(0.2D1) - 0.2D1) * 0.8D1 ) - CurlBasis(1,3) = 0.1D1 / 0.4D1 - IF (nj Element % Nodeindexes - - WorkBasis(1,1) = (2.0d0 - 2*v**2 - 2*Sqrt(2.0d0)*w + w**2)/(4.0d0 - 2*Sqrt(2.0d0)*w) - WorkBasis(1,2) = 0.0d0 - WorkBasis(1,3) = (u*(1.0d0 - (4*v**2)/(-2.0d0 + Sqrt(2.0d0)*w)**2))/(2.0d0*Sqrt(2.0d0)) - WorkCurlBasis(1,1) = (-2*Sqrt(2.0d0)*u*v)/(-2.0d0 + Sqrt(2.0d0)*w)**2 - WorkCurlBasis(1,2) = (-2*Sqrt(2.0d0) + 4*w - Sqrt(2.0d0)*w**2)/(-2.0d0 + Sqrt(2.0d0)*w)**2 - WorkCurlBasis(1,3) = (2.0d0*v)/(2.0d0 - Sqrt(2.0d0)*w) - - WorkBasis(2,1) = 0.0d0 - WorkBasis(2,2) = (2.0d0 - 2*u**2 - 2*Sqrt(2.0d0)*w + w**2)/(4.0d0 - 2*Sqrt(2.0d0)*w) - WorkBasis(2,3) = (v*(1.0d0 - (4*u**2)/(-2.0d0 + Sqrt(2.0d0)*w)**2))/(2.0d0*Sqrt(2.0d0)) - WorkCurlBasis(2,1) = (2*Sqrt(2.0d0) - 4*w + Sqrt(2.0d0)*w**2)/(-2.0d0 + Sqrt(2.0d0)*w)**2 - WorkCurlBasis(2,2) = (2*Sqrt(2.0d0)*u*v)/(-2.0d0 + Sqrt(2.0d0)*w)**2 - WorkCurlBasis(2,3) = (2*u)/(-2.0d0 + Sqrt(2.0d0)*w) - - ! ------------------------------------------------------------------- - ! Finally apply an order change and sign reversions if needed. - ! ------------------------------------------------------------------- - DO j=1,4 - FaceIndices(j) = Ind(SquareFaceMap(j)) - END DO - IF (Parallel) THEN - DO j=1,4 - FaceIndices(j) = Mesh % ParallelInfo % GlobalDOFs(FaceIndices(j)) - END DO - END IF - CALL SquareFaceDofsOrdering(I1,I2,D1,D2,FaceIndices) - - EdgeBasis(9,:) = D1 * WorkBasis(I1,:) - CurlBasis(9,:) = D1 * WorkCurlBasis(I1,:) - EdgeBasis(10,:) = D2 * WorkBasis(I2,:) - CurlBasis(10,:) = D2 * WorkCurlBasis(I2,:) - END IF - - CASE(7) - !-------------------------------------------------------------- - ! This branch is for handling prismatic (or wedge) elements - !-------------------------------------------------------------- - EdgeMap => GetEdgeMap(7) - Ind => Element % Nodeindexes - - IF (SecondOrder) THEN - !--------------------------------------------------------------- - ! The second-order element from the Nedelec's first family - ! (note that the lowest-order prism element is from a different - ! family). This element may not be optimally accurate if - ! the physical element is not affine. - !-------------------------------------------------------------- - h1 = 0.5d0 * (1-w) - dh1 = -0.5d0 - h2 = 0.5d0 * (1+w) - dh2 = 0.5d0 - h3 = h1 * h2 - dh3 = -0.5d0 * w - - ! --------------------------------------------------------- - ! The first and fourth edges ... - !-------------------------------------------------------- - ! The corresponding basis functions for the triangle: - !-------------------------------------------------------- - WorkBasis(1,1) = (3.0d0 - Sqrt(3.0d0)*v)/6.0d0 - WorkBasis(1,2) = u/(2.0d0*Sqrt(3.0d0)) - WorkCurlBasis(1,3) = 1.0d0/Sqrt(3.0d0) - WorkBasis(2,1) = -(u*(-3.0d0 + Sqrt(3.0d0)*v))/2.0d0 - WorkBasis(2,2) = (Sqrt(3.0d0)*u**2)/2.0d0 - WorkCurlBasis(2,3) = (3.0d0*Sqrt(3.0d0)*u)/2.0d0 - - i = EdgeMap(1,1) - j = EdgeMap(1,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - EdgeBasis(1,1:2) = WorkBasis(1,1:2) * h1 - CurlBasis(1,1) = -WorkBasis(1,2) * dh1 - CurlBasis(1,2) = WorkBasis(1,1) * dh1 - CurlBasis(1,3) = WorkCurlBasis(1,3) * h1 - EdgeBasis(2,1:2) = WorkBasis(2,1:2) * h1 - CurlBasis(2,1) = -WorkBasis(2,2) * dh1 - CurlBasis(2,2) = WorkBasis(2,1) * dh1 - CurlBasis(2,3) = WorkCurlBasis(2,3) * h1 - IF (nj GetEdgeMap(8) - Ind => Element % Nodeindexes - - IF (SecondOrder) THEN - !--------------------------------------------------------------- - ! The second-order element from the Nedelec's first family - ! (note that the lowest-order brick element is from a different - ! family). This element may not be optimally accurate if - ! the physical element is not affine. - !-------------------------------------------------------------- - - ! Edges 12 and 43 ... - DO q=1,2 - k = 2*q-1 ! Edge number k: 1 ~ 12 and 3 ~ 43 - i = EdgeMap(k,1) - j = EdgeMap(k,2) - ni = Element % NodeIndexes(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Element % NodeIndexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - - EdgeBasis(2*(k-1)+1,1) = 0.5d0 * LineNodalPBasis(1,w) * LineNodalPBasis(q,v) - CurlBasis(2*(k-1)+1,2) = 0.5d0 * (-0.5d0) * LineNodalPBasis(q,v) - CurlBasis(2*(k-1)+1,3) = -0.5d0 * LineNodalPBasis(1,w) * dLineNodalPBasis(q,v) - EdgeBasis(2*(k-1)+2,1) = 1.5d0 * LineNodalPBasis(1,w) * u * LineNodalPBasis(q,v) - CurlBasis(2*(k-1)+2,2) = 1.5d0 * (-0.5d0) * u * LineNodalPBasis(q,v) - CurlBasis(2*(k-1)+2,3) = -1.5d0 * LineNodalPBasis(1,w) * u * dLineNodalPBasis(q,v) - IF (nj b x n. The resulting field transforms under the usual - ! Piola transform (like div-conforming field). For a general surface element - ! embedded in 3D we return B(f(p))=1/sqrt(a) F(b x n) where a is the determinant of - ! the metric tensor, F=[a1 a2] with a1 and a2 surface basis vectors and (b x n) is - ! considered to be 2-vector (the trivial component ignored). Note that asking simultaneously - ! for the curl of the basis is not an expected combination. - DO j=1,DOFs - WorkBasis(1,1:2) = EdgeBasis(j,1:2) - EdgeBasis(j,1) = WorkBasis(1,2) - EdgeBasis(j,2) = -WorkBasis(1,1) - END DO - IF (PerformPiolaTransform) THEN - DO j=1,DOFs - DO k=1,cdim - B(k) = SUM( LF(k,1:dim) * EdgeBasis(j,1:dim) ) / DetJ - END DO - EdgeBasis(j,1:cdim) = B(1:cdim) - END DO - END IF - ELSE - IF (PerformPiolaTransform) THEN - DO j=1,DOFs - DO k=1,cdim - B(k) = SUM( LG(k,1:dim) * EdgeBasis(j,1:dim) ) - END DO - EdgeBasis(j,1:cdim) = B(1:cdim) - ! The returned spatial curl in the case cdim=3 and dim=2 handled here - ! has limited usability. This handles only a transformation of - ! the type x_3 = p_3: - CurlBasis(j,3) = 1.0d0/DetJ * CurlBasis(j,3) - END DO - END IF - END IF - - ! Make the returned value DetF to act as a metric term for integration - ! over the volume of the element: - DetF = DetJ - - ! ---------------------------------------------------------------------- - ! Get global first derivatives of the nodal basis functions if wanted: - ! ---------------------------------------------------------------------- - IF ( PRESENT(dBasisdx) ) THEN - dBasisdx = 0.0d0 - DO i=1,n - DO j=1,cdim - DO k=1,dim - dBasisdx(i,j) = dBasisdx(i,j) + dLBasisdx(i,k)*LG(j,k) - END DO - END DO - END DO - END IF - - END IF - - IF(PRESENT(F)) F = LF - IF(PRESENT(G)) G = LG - IF(PRESENT(RotBasis)) RotBasis(1:DOFs,:) = CurlBasis(1:DOFs,:) -!----------------------------------------------------------------------------- - END FUNCTION EdgeElementInfo -!------------------------------------------------------------------------------ - - - -!---------------------------------------------------------------------------- - SUBROUTINE TriangleFaceDofsOrdering(I1,I2,D1,D2,Ind) -!----------------------------------------------------------------------------- -! This is used for selecting what additional basis functions are associated -! with a triangular face in the case of second-order approximation. -! ---------------------------------------------------------------------------- - INTEGER :: I1, I2, Ind(4) - REAL(KIND=dp) :: D1, D2 -!--------------------------------------------------------------------------- - INTEGER :: k, A -! -------------------------------------------------------------------------- - D1 = 1.0d0 - D2 = 1.0d0 - IF ( Ind(1) < Ind(2) ) THEN - k = 1 - ELSE - k = 2 - END IF - IF ( Ind(k) > Ind(3) ) THEN - k = 3 - END IF - A = k - - SELECT CASE(A) - CASE(1) - IF (Ind(3) > Ind(2)) THEN - ! C = 3 - I1 = 1 - I2 = 2 - ELSE - ! C = 2 - I1 = 2 - I2 = 1 - END IF - CASE(2) - IF (Ind(3) > Ind(1)) THEN - ! C = 3 - I1 = 1 - I2 = 3 - D1 = -1.0d0 - ELSE - ! C = 1 - I1 = 3 - I2 = 1 - D2 = -1.0d0 - END IF - CASE(3) - IF (Ind(2) > Ind(1)) THEN - ! C = 2 - I1 = 2 - I2 = 3 - ELSE - ! C = 1 - I1 = 3 - I2 = 2 - END IF - D1 = -1.0d0 - D2 = -1.0d0 - CASE DEFAULT - CALL Fatal('ElementDescription::TriangleFaceDofsOrdering','Erratic square face Indices') - END SELECT -!--------------------------------------------------------- - END SUBROUTINE TriangleFaceDofsOrdering -!----------------------------------------------------------- - - -!------------------------------------------------------------- - SUBROUTINE TriangleFaceDofsOrdering2(t,s,Ind) -!------------------------------------------------------------------------------- -! Returns two unit vectors t and s for spanning constant vector fields -! defined on a triangular face. As a rule for orientation, the vector t is defined -! as t = Grad L_B - Grad L_A where L_A and L_B are the Lagrange basis functions -! associated with the nodes that has the smallest global indices A and B (A Ind(3) ) THEN - k = 3 - END IF - A = k - - SELECT CASE(A) - CASE(1) - IF ( Ind(2) < Ind(3) ) THEN ! B=2, tangent = AB = 12 - t(1) = 1.0d0 - t(2) = 0.0 - s(1) = 0.0d0 - s(2) = 1.0d0 - ELSE ! B=3, tangent = AB = 13 - t(1) = 0.5d0 - t(2) = Sqrt(3.0d0)/2.0d0 - s(1) = Sqrt(3.0d0)/2.0d0 - s(2) = -0.5d0 - END IF - CASE(2) - IF ( Ind(1) < Ind(3) ) THEN ! B=1, tangent = AB = 21 - t(1) = -1.0d0 - t(2) = 0.0 - s(1) = 0.0d0 - s(2) = 1.0d0 - ELSE ! B=3, tangent = AB = 23 - t(1) = -0.5d0 - t(2) = Sqrt(3.0d0)/2.0d0 - s(1) = -Sqrt(3.0d0)/2.0d0 - s(2) = -0.5d0 - END IF - CASE(3) - IF ( Ind(1) < Ind(2) ) THEN ! B=1, tangent = AB = 31 - t(1) = -0.5d0 - t(2) = -Sqrt(3.0d0)/2.0d0 - s(1) = Sqrt(3.0d0)/2.0d0 - s(2) = -0.5d0 - ELSE ! B=2, tangent = AB = 32 - t(1) = 0.5d0 - t(2) = -Sqrt(3.0d0)/2.0d0 - s(1) = -Sqrt(3.0d0)/2.0d0 - s(2) = -0.5d0 - END IF - CASE DEFAULT - CALL Fatal('ElementDescription::TriangleFaceDofsOrdering','Erratic square face Indices') - END SELECT -!--------------------------------------------------------- - END SUBROUTINE TriangleFaceDofsOrdering2 -!----------------------------------------------------------- - - -!--------------------------------------------------------- - SUBROUTINE SquareFaceDofsOrdering(I1,I2,D1,D2,Ind) -!----------------------------------------------------------- - INTEGER :: I1, I2, Ind(4) - REAL(KIND=dp) :: D1, D2 -!---------------------------------------------------------- - INTEGER :: i, j, k, l, A -! ------------------------------------------------------------------- -! Find input for applying an order change and sign reversions to two -! basis functions associated with a square face. To this end, -! find nodes A, B, C such that A has the minimal global index, -! AB and AC are edges, with C having the largest global index. -! Then AB gives the positive direction for the first face DOF and -! AC gives the positive direction for the second face DOF. -! REMARK: This convention must be followed when creating basis -! functions for other element types which are intended to be compatible -! with the element type to which this rule is applied. -! ------------------------------------------------------------------- - i = 1 - j = 2 - IF ( Ind(i) < Ind(j) ) THEN - k = i - ELSE - k = j - END IF - i = 4 - j = 3 - IF ( Ind(i) < Ind(j) ) THEN - l = i - ELSE - l = j - END IF - IF ( Ind(k) > Ind(l) ) THEN - k = l - END IF - A = k - - SELECT CASE(A) - CASE(1) - IF ( Ind(2) < Ind(4) ) THEN - I1 = 1 - I2 = 2 - D1 = 1.0d0 - D2 = 1.0d0 - ELSE - I1 = 2 - I2 = 1 - D1 = 1.0d0 - D2 = 1.0d0 - END IF - CASE(2) - IF ( Ind(3) < Ind(1) ) THEN - I1 = 2 - I2 = 1 - D1 = 1.0d0 - D2 = -1.0d0 - ELSE - I1 = 1 - I2 = 2 - D1 = -1.0d0 - D2 = 1.0d0 - END IF - CASE(3) - IF ( Ind(4) < Ind(2) ) THEN - I1 = 1 - I2 = 2 - D1 = -1.0d0 - D2 = -1.0d0 - ELSE - I1 = 2 - I2 = 1 - D1 = -1.0d0 - D2 = -1.0d0 - END IF - CASE(4) - IF ( Ind(1) < Ind(3) ) THEN - I1 = 2 - I2 = 1 - D1 = -1.0d0 - D2 = 1.0d0 - ELSE - I1 = 1 - I2 = 2 - D1 = 1.0d0 - D2 = -1.0d0 - END IF - CASE DEFAULT - CALL Fatal('ElementDescription::SquareFaceDofsOrdering','Erratic square face Indices') - END SELECT -!---------------------------------------------------------- - END SUBROUTINE SquareFaceDofsOrdering -!---------------------------------------------------------- - -!---------------------------------------------------------------------------------- -!> Returns data for rearranging H(curl)-conforming basis functions so that -!> compatibility with the convention for defining global DOFs is attained. -!> If n basis function value have already been tabulated in the default order -!> as BasisArray(1:n,:), then SignVec(1:n) * BasisArray(PermVec(1:n),:) gives -!> the basis vector values corresponding to the global DOFs. -!> TO DO: support for second-order basis functions, triangles and quads missing -!------------------------------------------------------------------------------------ - SUBROUTINE ReorderingAndSignReversionsData(Element,Nodes,PermVec,SignVec) -!------------------------------------------------------------------------------------- - IMPLICIT NONE - - TYPE(Element_t), TARGET :: Element !< Element structure - TYPE(Nodes_t) :: Nodes !< Data corresponding to the classic element nodes - INTEGER :: PermVec(:) !< At exit the permution vector for performing reordering - REAL(KIND=dp) :: SignVec(:) !< At exit the vector for performing sign changes -!--------------------------------------------------------------------------------------------------- - TYPE(Mesh_t), POINTER :: Mesh - INTEGER, POINTER :: EdgeMap(:,:), Ind(:) - INTEGER :: SquareFaceMap(4), BrickFaceMap(6,4), PrismSquareFaceMap(3,4), DOFs, i, j, k - INTEGER :: FaceIndices(4), I1, I2, ni, nj - REAL(KIND=dp) :: D1, D2 - LOGICAL :: Parallel -!--------------------------------------------------------------------------------------------------- - Mesh => CurrentModel % Solver % Mesh - !Parallel = ParEnv % PEs>1 - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - - SignVec = 1.0d0 - Ind => Element % Nodeindexes - - SELECT CASE( Element % TYPE % ElementCode / 100 ) - !CASE(3) needs to be done - - !CASE(4) needs to be done - - CASE(5) - ! NOTE: The Nedelec second family is not yet supported - EdgeMap => GetEdgeMap(5) - DO k=1,6 - i = EdgeMap(k,1) - j = EdgeMap(k,2) - ni = Ind(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Ind(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj GetEdgeMap(6) - DO k=1,8 - i = EdgeMap(k,1) - j = EdgeMap(k,2) - ni = Ind(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Ind(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj GetEdgeMap(7) - DO k=1,9 - i = EdgeMap(k,1) - j = EdgeMap(k,2) - ni = Ind(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Ind(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj GetEdgeMap(8) - DO k=1,12 - i = EdgeMap(k,1) - j = EdgeMap(k,2) - ni = Ind(i) - IF (Parallel) ni=Mesh % ParallelInfo % GlobalDOFs(ni) - nj = Ind(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - IF (nj This subroutine contains an older design for providing edge element basis functions -!> of the lowest-degree. Obtaining optimal accuracy with these elements may require that -!> the element map is affine, while the edge basis functions given by the newer design -!> (the function EdgeElementInfo) should also work on general meshes. -!------------------------------------------------------------------------ - SUBROUTINE GetEdgeBasis( Element, WBasis, RotWBasis, Basis, dBasisdx ) -!------------------------------------------------------------------------ - TYPE(Element_t),TARGET :: Element - REAL(KIND=dp) :: WBasis(:,:), RotWBasis(:,:), Basis(:), dBasisdx(:,:) -!------------------------------------------------------------------------ - TYPE(Element_t),POINTER :: Edge - TYPE(Mesh_t), POINTER :: Mesh - TYPE(Nodes_t), SAVE :: Nodes - REAL(KIND=dp) :: u,v,w,dudx(3,3),du(3),Base,dBase(3),tBase(3), & - rBase(3),triBase(3),dtriBase(3,3), G(3,3), F(3,3), detF, detG, & - EdgeBasis(8,3), CurlBasis(8,3) - LOGICAL :: Parallel,stat - INTEGER :: i,j,k,n,nj,nk,i1,i2 - INTEGER, POINTER :: EdgeMap(:,:) -!------------------------------------------------------------------------ - Mesh => CurrentModel % Solver % Mesh - Parallel = ASSOCIATED(Mesh % ParallelInfo % Interface) - - IF (Element % TYPE % BasisFunctionDegree>1) THEN - CALL Fatal('GetEdgeBasis',"Can't handle but linear elements, sorry.") - END IF - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(4,7,8) - n = Element % TYPE % NumberOfNodes - u = SUM(Basis(1:n)*Element % TYPE % NodeU(1:n)) - v = SUM(Basis(1:n)*Element % TYPE % NodeV(1:n)) - w = SUM(Basis(1:n)*Element % TYPE % NodeW(1:n)) - - dudx(1,:) = MATMUL(Element % TYPE % NodeU(1:n),dBasisdx(1:n,:)) - dudx(2,:) = MATMUL(Element % TYPE % NodeV(1:n),dBasisdx(1:n,:)) - dudx(3,:) = MATMUL(Element % TYPE % NodeW(1:n),dBasisdx(1:n,:)) - - triBase(1) = 1-u-v - triBase(2) = u - triBase(3) = v - - dtriBase(1,:) = -dudx(1,:)-dudx(2,:) - dtriBase(2,:) = dudx(1,:) - dtriBase(3,:) = dudx(2,:) - CASE(6) - n = Element % TYPE % NumberOfNodes - u = SUM(Basis(1:n)*Element % TYPE % NodeU(1:n)) - v = SUM(Basis(1:n)*Element % TYPE % NodeV(1:n)) - w = SUM(Basis(1:n)*Element % TYPE % NodeW(1:n)) - - G(1,:) = MATMUL(Element % TYPE % NodeU(1:n),dBasisdx(1:n,:)) - G(2,:) = MATMUL(Element % TYPE % NodeV(1:n),dBasisdx(1:n,:)) - G(3,:) = MATMUL(Element % TYPE % NodeW(1:n),dBasisdx(1:n,:)) - - detG = G(1,1) * ( G(2,2)*G(3,3) - G(2,3)*G(3,2) ) + & - G(1,2) * ( G(2,3)*G(3,1) - G(2,1)*G(3,3) ) + & - G(1,3) * ( G(2,1)*G(3,2) - G(2,2)*G(3,1) ) - detF = 1.0d0/detG - CALL InvertMatrix3x3(G,F,detG) - - !------------------------------------------------------------ - ! The basis functions spanning the reference element space and - ! their Curl with respect to the local coordinates - ! ------------------------------------------------------------ - EdgeBasis(1,1) = (1.0d0 - v - w)/4.0d0 - EdgeBasis(1,2) = 0.0d0 - EdgeBasis(1,3) = (u*(-1.0d0 + v + w))/(4.0d0*(-1.0d0 + w)) - CurlBasis(1,1) = u/(4.0d0*(-1.0d0 + w)) - CurlBasis(1,2) = -(-2.0d0 + v + 2.0d0*w)/(4.0d0*(-1.0d0 + w)) - CurlBasis(1,3) = 0.25d0 - - EdgeBasis(2,1) = 0.0d0 - EdgeBasis(2,2) = (1.0d0 + u - w)/4.0d0 - EdgeBasis(2,3) = (v*(1.0d0 + u - w))/(4.0d0 - 4.0d0*w) - CurlBasis(2,1) = (2.0d0 + u - 2.0d0*w)/(4.0d0 - 4.0d0*w) - CurlBasis(2,2) = v/(4.0d0*(-1.0d0 + w)) - CurlBasis(2,3) = 0.25d0 - - EdgeBasis(3,1) = (1.0d0 + v - w)/4.0d0 - EdgeBasis(3,2) = 0.0d0 - EdgeBasis(3,3) = (u*(1.0d0 + v - w))/(4.0d0 - 4.0d0*w) - CurlBasis(3,1) = u/(4.0d0 - 4.0d0*w) - CurlBasis(3,2) = (2.0d0 + v - 2.0d0*w)/(4.0d0*(-1.0d0 + w)) - CurlBasis(3,3) = -0.25d0 - - EdgeBasis(4,1) = 0.0d0 - EdgeBasis(4,2) = (1.0d0 - u - w)/4.0d0 - EdgeBasis(4,3) = (v*(-1.0d0 + u + w))/(4.0d0*(-1.0d0 + w)) - CurlBasis(4,1) = (-2.0d0 + u + 2.0d0*w)/(4.0d0*(-1.0d0 + w)) - CurlBasis(4,2) = v/(4.0d0 - 4.0d0*w) - CurlBasis(4,3) = -0.25d0 - - EdgeBasis(5,1) = (w*(-1.0d0 + v + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(5,2) = (w*(-1.0d0 + u + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(5,3) = (-((-1.0d0 + v)*(-1.0d0 + w)**2) + u*(v - (-1.0d0 + w)**2 - 2.0d0*v*w))/& - (4.0d0*(-1.0d0 + w)**2) - CurlBasis(5,1) = -(-1.0d0 + u + w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(5,2) = (-1.0d0 + v + w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(5,3) = 0.0d0 - - EdgeBasis(6,1) = -(w*(-1.0d0 + v + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(6,2) = (w*(-1.0d0 - u + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(6,3) = (-((-1.0d0 + v)*(-1.0d0 + w)**2) + u*((-1.0d0 + w)**2 + v*(-1.0d0 + 2.0d0*w)))/& - (4.0d0*(-1.0d0 + w)**2) - CurlBasis(6,1) = (1.0d0 + u - w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(6,2) = -(-1.0d0 + v + w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(6,3) = 0.0d0 - - EdgeBasis(7,1) = ((1.0d0 + v - w)*w)/(4.0d0*(-1.0d0 + w)) - EdgeBasis(7,2) = ((1.0d0 + u - w)*w)/(4.0d0*(-1.0d0 + w)) - EdgeBasis(7,3) = ((1.0d0 + v)*(-1.0d0 + w)**2 + u*(v + (-1.0d0 + w)**2 - 2.0d0*v*w))/& - (4.0d0*(-1.0d0 + w)**2) - CurlBasis(7,1) = (1.0d0 + u - w)/(2.0d0 - 2.0d0*w) - CurlBasis(7,2) = (1.0d0 + v - w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(7,3) = 0.0d0 - - EdgeBasis(8,1) = (w*(-1.0d0 - v + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(8,2) = -(w*(-1.0d0 + u + w))/(4.0d0*(-1.0d0 + w)) - EdgeBasis(8,3) = ((1.0d0 + v)*(-1.0d0 + w)**2 - u*(v + (-1.0d0 + w)**2 - 2.0d0*v*w))/& - (4.0d0*(-1.0d0 + w)**2) - CurlBasis(8,1) = (-1.0d0 + u + w)/(2.0d0*(-1.0d0 + w)) - CurlBasis(8,2) = (1.0d0 + v - w)/(2.0d0 - 2.0d0*w) - CurlBasis(8,3) = 0.0d0 - - END SELECT - - EdgeMap => GetEdgeMap(Element % TYPE % ElementCode / 100) - DO i=1,SIZE(Edgemap,1) - j = EdgeMap(i,1); k = EdgeMap(i,2) - - nj = Element % Nodeindexes(j) - IF (Parallel) nj=Mesh % ParallelInfo % GlobalDOFs(nj) - nk = Element % Nodeindexes(k) - IF (Parallel) nk=Mesh % ParallelInfo % GlobalDOFs(nk) - - SELECT CASE(Element % TYPE % ElementCode / 100) - CASE(3,5) - WBasis(i,:) = Basis(j)*dBasisdx(k,:) - Basis(k)*dBasisdx(j,:) - - RotWBasis(i,1) = 2.0_dp * ( dBasisdx(j,2) * dBasisdx(k,3) - & - dBasisdx(j,3) * dBasisdx(k,2) ) - RotWBasis(i,2) = 2.0_dp * ( dBasisdx(j,3) * dBasisdx(k,1) - & - dBasisdx(j,1) * dBasisdx(k,3) ) - RotWBasis(i,3) = 2.0_dp * ( dBasisdx(j,1) * dBasisdx(k,2) - & - dBasisdx(j,2) * dBasisdx(k,1) ) - - CASE(6) - !----------------------------------------------------------------------- - ! Create the referential description of basis functions and their - ! spatial curl on the physical element via applying the Piola transform: - !----------------------------------------------------------------------- - DO k=1,3 - WBasis(i,k) = SUM( G(1:3,k) * EdgeBasis(i,1:3) ) - END DO - DO k=1,3 - RotWBasis(i,k) = 1.0d0/DetF * SUM( F(k,1:3) * CurlBasis(i,1:3) ) - END DO - - CASE(7) - SELECT CASE(i) - CASE(1) - j=1;k=2; Base=(1-w)/2; dBase=-dudx(3,:)/2 - CASE(2) - j=2;k=3; Base=(1-w)/2; dBase=-dudx(3,:)/2 - CASE(3) - j=3;k=1; Base=(1-w)/2; dBase=-dudx(3,:)/2 - CASE(4) - j=1;k=2; Base=(1+w)/2; dBase= dudx(3,:)/2 - CASE(5) - j=2;k=3; Base=(1+w)/2; dBase= dudx(3,:)/2 - CASE(6) - j=3;k=1; Base=(1+w)/2; dBase= dudx(3,:)/2 - CASE(7) - Base=triBase(1); dBase=dtriBase(1,:); du=dudx(3,:)/2 - CASE(8) - Base=triBase(2); dBase=dtriBase(2,:); du=dudx(3,:)/2 - CASE(9) - Base=triBase(3); dBase=dtriBase(3,:); du=dudx(3,:)/2 - END SELECT - - IF(i<=6) THEN - tBase = (triBase(j)*dtriBase(k,:)-triBase(k)*dtriBase(j,:)) - rBase(1) = 2*Base*(dtriBase(j,2)*dtriBase(k,3)-dtriBase(k,2)*dtriBase(j,3)) + & - dBase(2)*tBase(3) - dBase(3)*tBase(2) - - rBase(2) = 2*Base*(dtriBase(j,3)*dtriBase(k,1)-dtriBase(k,3)*dtriBase(j,1)) + & - dBase(3)*tBase(1) - dBase(1)*tBase(3) - - rBase(3) = 2*Base*(dtriBase(j,1)*dtriBase(k,2)-dtriBase(k,1)*dtriBase(j,2)) + & - dBase(1)*tBase(2) - dBase(2)*tBase(1) - - RotWBasis(i,:)=rBase - WBasis(i,:)=tBase*Base - ELSE - WBasis(i,:)=Base*du - RotWBasis(i,1)=(dBase(2)*du(3) - dBase(3)*du(2)) - RotWBasis(i,2)=(dBase(3)*du(1) - dBase(1)*du(3)) - RotWBasis(i,3)=(dBase(1)*du(2) - dBase(2)*du(1)) - END IF - CASE(4) - SELECT CASE(i) - CASE(1) - du=dudx(1,:); Base=(1-v)*(1-w) - dBase(:)=-dudx(2,:)*(1-w)-(1-v)*dudx(3,:) - CASE(2) - du=dudx(2,:); Base=(1+u)*(1-w) - dBase(:)= dudx(1,:)*(1-w)-(1+u)*dudx(3,:) - CASE(3) - du=-dudx(1,:); Base=(1+v)*(1-w) - dBase(:)= dudx(2,:)*(1-w)-(1+v)*dudx(3,:) - CASE(4) - du=-dudx(2,:); Base=(1-u)*(1-w) - dBase(:)=-dudx(1,:)*(1-w)-(1-u)*dudx(3,:) - END SELECT - - wBasis(i,:) = Base*du/n - RotWBasis(i,1)=(dBase(2)*du(3) - dBase(3)*du(2))/n - RotWBasis(i,2)=(dBase(3)*du(1) - dBase(1)*du(3))/n - RotWBasis(i,3) = (dBase(1)*du(2) - dBase(2)*du(1))/n - CASE(8) - SELECT CASE(i) - CASE(1) - du=dudx(1,:); Base=(1-v)*(1-w) - dBase(:)=-dudx(2,:)*(1-w)-(1-v)*dudx(3,:) - CASE(2) - du=dudx(2,:); Base=(1+u)*(1-w) - dBase(:)= dudx(1,:)*(1-w)-(1+u)*dudx(3,:) - CASE(3) - du=dudx(1,:); Base=(1+v)*(1-w) - dBase(:)= dudx(2,:)*(1-w)-(1+v)*dudx(3,:) - CASE(4) - du=dudx(2,:); Base=(1-u)*(1-w) - dBase(:)=-dudx(1,:)*(1-w)-(1-u)*dudx(3,:) - CASE(5) - du=dudx(1,:); Base=(1-v)*(1+w) - dBase(:)=-dudx(2,:)*(1+w)+(1-v)*dudx(3,:) - CASE(6) - du=dudx(2,:); Base=(1+u)*(1+w) - dBase(:)= dudx(1,:)*(1+w)+(1+u)*dudx(3,:) - CASE(7) - du=dudx(1,:); Base=(1+v)*(1+w) - dBase(:)= dudx(2,:)*(1+w)+(1+v)*dudx(3,:) - CASE(8) - du=dudx(2,:); Base=(1-u)*(1+w) - dBase(:)=-dudx(1,:)*(1+w)+(1-u)*dudx(3,:) - CASE(9) - du=dudx(3,:); Base=(1-u)*(1-v) - dBase(:)=-dudx(1,:)*(1-v)-(1-u)*dudx(2,:) - CASE(10) - du=dudx(3,:); Base=(1+u)*(1-v) - dBase(:)= dudx(1,:)*(1-v)-(1+u)*dudx(2,:) - CASE(11) - du=dudx(3,:); Base=(1+u)*(1+v) - dBase(:)= dudx(1,:)*(1+v)+(1+u)*dudx(2,:) - CASE(12) - du=dudx(3,:); Base=(1-u)*(1+v) - dBase(:)=-dudx(1,:)*(1+v)+(1-u)*dudx(2,:) - END SELECT - - wBasis(i,:)=Base*du/n - RotWBasis(i,1)=(dBase(2)*du(3) - dBase(3)*du(2))/n - RotWBasis(i,2)=(dBase(3)*du(1) - dBase(1)*du(3))/n - RotWBasis(i,3)=(dBase(1)*du(2) - dBase(2)*du(1))/n - CASE DEFAULT - CALL Fatal( 'Edge Basis', 'Not implemented for this element type.') - END SELECT - - IF( nk < nj ) THEN - WBasis(i,:) = -WBasis(i,:); RotWBasis(i,:) = -RotWBasis(i,:) - END IF - END DO -!------------------------------------------------------------------------------ - END SUBROUTINE GetEdgeBasis -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Compute contravariant metric tensor (=J^TJ)^-1 of element coordinate -!> system, and square root of determinant of covariant metric tensor -!> (=sqrt(det(J^TJ))) -!------------------------------------------------------------------------------ - FUNCTION ElementMetric(nDOFs,Elm,Nodes,Metric,DetG,dLBasisdx,LtoGMap) RESULT(Success) -!------------------------------------------------------------------------------ - INTEGER :: nDOFs !< Number of active nodes in element - TYPE(Element_t) :: Elm !< Element structure - TYPE(Nodes_t) :: Nodes !< Element nodal coordinates - REAL(KIND=dp) :: Metric(:,:) !< Contravariant metric tensor - REAL(KIND=dp) :: dLBasisdx(:,:) !< Derivatives of element basis function with respect to local coordinates - REAL(KIND=dp) :: DetG !< SQRT of determinant of metric tensor - REAL(KIND=dp) :: LtoGMap(3,3) !< Transformation to obtain the referencial description of the spatial gradient - LOGICAL :: Success !< Returns .FALSE. if element is degenerate -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: dx(3,3),G(3,3),GI(3,3),s - REAL(KIND=dp), DIMENSION(:), POINTER :: x,y,z - INTEGER :: GeomId - - INTEGER :: cdim,dim,i,j,k,n -!------------------------------------------------------------------------------ - success = .TRUE. - - x => Nodes % x - y => Nodes % y - z => Nodes % z - - cdim = CoordinateSystemDimension() - n = MIN( SIZE(x), nDOFs ) - dim = elm % TYPE % DIMENSION - -!------------------------------------------------------------------------------ -! Partial derivatives of global coordinates with respect to local coordinates -!------------------------------------------------------------------------------ - DO i=1,dim - dx(1,i) = SUM( x(1:n) * dLBasisdx(1:n,i) ) - dx(2,i) = SUM( y(1:n) * dLBasisdx(1:n,i) ) - dx(3,i) = SUM( z(1:n) * dLBasisdx(1:n,i) ) - END DO -!------------------------------------------------------------------------------ -! Compute the covariant metric tensor of the element coordinate system -!------------------------------------------------------------------------------ - DO i=1,dim - DO j=1,dim - s = 0.0d0 - DO k=1,cdim - s = s + dx(k,i)*dx(k,j) - END DO - G(i,j) = s - END DO - END DO -!------------------------------------------------------------------------------ -! Convert the metric to contravariant base, and compute the SQRT(DetG) -!------------------------------------------------------------------------------ - SELECT CASE( dim ) -!------------------------------------------------------------------------------ -! Line elements -!------------------------------------------------------------------------------ - CASE (1) - DetG = G(1,1) - - IF ( DetG <= TINY( DetG ) ) GOTO 100 - - Metric(1,1) = 1.0d0 / DetG - DetG = SQRT( DetG ) - -!------------------------------------------------------------------------------ -! Surface elements -!------------------------------------------------------------------------------ - CASE (2) - DetG = ( G(1,1)*G(2,2) - G(1,2)*G(2,1) ) - - IF ( DetG <= TINY( DetG ) ) GOTO 100 - - Metric(1,1) = G(2,2) / DetG - Metric(1,2) = -G(1,2) / DetG - Metric(2,1) = -G(2,1) / DetG - Metric(2,2) = G(1,1) / DetG - DetG = SQRT(DetG) - -!------------------------------------------------------------------------------ -! Volume elements -!------------------------------------------------------------------------------ - CASE (3) - DetG = G(1,1) * ( G(2,2)*G(3,3) - G(2,3)*G(3,2) ) + & - G(1,2) * ( G(2,3)*G(3,1) - G(2,1)*G(3,3) ) + & - G(1,3) * ( G(2,1)*G(3,2) - G(2,2)*G(3,1) ) - - IF ( DetG <= TINY( DetG ) ) GOTO 100 - - CALL InvertMatrix3x3( G,GI,detG ) - Metric = GI - DetG = SQRT(DetG) - END SELECT - -!-------------------------------------------------------------------------------------- -! Construct a transformation X = LtoGMap such that (grad B)(f(p)) = X(p) Grad b(p), -! with Grad the gradient with respect to the reference element coordinates p and -! the referencial description of the spatial field B(x) satisfying B(f(p)) = b(p). -! If cdim > dim (e.g. a surface embedded in the 3-dimensional space), X is -! the pseudo-inverse of (Grad f)^{T}. -!------------------------------------------------------------------------------- - DO i=1,cdim - DO j=1,dim - s = 0.0d0 - DO k=1,dim - s = s + dx(i,k) * Metric(k,j) - END DO - LtoGMap(i,j) = s - END DO - END DO - -! Return here also implies success = .TRUE. - RETURN - - -100 Success = .FALSE. - WRITE( Message,'(A,I0,A,I0)') 'Degenerate ',dim,'D element: ',Elm % ElementIndex - CALL Error( 'ElementMetric', Message ) - - IF( ASSOCIATED( Elm % BoundaryInfo ) ) THEN - WRITE( Message,'(A,I0,A,ES12.3)') 'Boundary Id: ',Elm % BoundaryInfo % Constraint,' DetG:',DetG - ELSE - WRITE( Message,'(A,I0,A,ES12.3)') 'Body Id: ',Elm % BodyId,' DetG:',DetG - END IF - CALL Info( 'ElementMetric', Message, Level=3 ) - - DO i=1,n - WRITE( Message,'(A,I0,A,3ES12.3)') 'Node: ',i,' Coord:',x(i),y(i),z(i) - CALL Info( 'ElementMetric', Message, Level=3 ) - END DO - DO i=2,n - WRITE( Message,'(A,I0,A,3ES12.3)') 'Node: ',i,' dCoord:',& - x(i)-x(1),y(i)-y(1),z(i)-z(1) - CALL Info( 'ElementMetric', Message, Level=3 ) - END DO - IF ( cdim < dim ) THEN - WRITE( Message,'(A,I0,A,I0)') 'Element dim larger than meshdim: ',dim,' vs. ',cdim - CALL Info( 'ElementMetric', Message, Level=3 ) - END IF - -!------------------------------------------------------------------------------ - END FUNCTION ElementMetric -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ - FUNCTION ElementMetricVec( Elm, Nodes, nc, ndof, DetJ, nbmax, dLBasisdx, LtoGMap) RESULT(AllSuccess) -!------------------------------------------------------------------------------ - TYPE(Element_t) :: Elm !< Element structure - TYPE(Nodes_t) :: Nodes !< element nodal coordinates - INTEGER, INTENT(IN) :: nc !< Number of points to map - INTEGER :: ndof !< Number of active nodes in element - REAL(KIND=dp) :: DetJ(VECTOR_BLOCK_LENGTH) !< SQRT of determinant of element coordinate metric at each point - INTEGER, INTENT(IN) :: nbmax !< Maximum total number of basis functions in local basis - REAL(KIND=dp) :: dLBasisdx(VECTOR_BLOCK_LENGTH,nbmax,3) !< Derivatives of element basis function with - !< respect to local coordinates at each point - REAL(KIND=dp) :: LtoGMap(VECTOR_BLOCK_LENGTH,3,3) !< Mapping between local and global coordinates - LOGICAL :: AllSuccess !< Returns .FALSE. if some point in element is degenerate -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: dx(VECTOR_BLOCK_LENGTH,3,3) - REAL(KIND=dp) :: Metric(VECTOR_BLOCK_LENGTH,6), & - G(VECTOR_BLOCK_LENGTH,6) ! Symmetric Metric(nc,3,3) and G(nc,3,3) - - REAL(KIND=dp) :: s - INTEGER :: cdim,dim,i,j,k,l,n,ip, jj, kk - INTEGER :: ldbasis, ldxyz, utind -!DIR$ ATTRIBUTES ALIGN:64::Metric -!DIR$ ATTRIBUTES ALIGN:64::dx -!DIR$ ATTRIBUTES ALIGN:64::G -!DIR$ ASSUME_ALIGNED dLBasisdx:64, LtoGMap:64, DetJ:64 - !------------------------------------------------------------------------------ - AllSuccess = .TRUE. - - ! Coordinates (single array) - n = MIN( SIZE(Nodes % x, 1), ndof ) - - ! Dimensions (coordinate system and element) - cdim = CoordinateSystemDimension() - dim = elm % TYPE % DIMENSION - - ! Leading dimensions for local basis and coordinate arrays - ldbasis = SIZE(dLBasisdx, 1) - ldxyz = SIZE(Nodes % xyz, 1) - - ! For linear, extruded and otherwise regular elements mapping has to be computed - ! only once, the problem is to identify these cases... - !------------------------------------------------------------------------------ - ! Partial derivatives of global coordinates with respect to local coordinates - !------------------------------------------------------------------------------ - ! Avoid DGEMM calls for nc small - IF (nc < VECTOR_SMALL_THRESH) THEN - DO l=1,dim - DO j=1,3 - dx(1:nc,j,l)=REAL(0,dp) - DO k=1,n -!DIR$ UNROLL - DO i=1,nc - dx(i,j,l)=dx(i,j,l)+dLBasisdx(i,k,l)*Nodes % xyz(k,j) - END DO - END DO - END DO - END DO - ELSE - DO i=1,dim - CALL DGEMM('N','N',nc, 3, n, & - REAL(1,dp), dLbasisdx(1,1,i), ldbasis, & - Nodes % xyz, ldxyz, REAL(0, dp), dx(1,1,i), VECTOR_BLOCK_LENGTH) - END DO - END IF - !------------------------------------------------------------------------------ - ! Compute the covariant metric tensor of the element coordinate system (symmetric) - !------------------------------------------------------------------------------ - ! Linearized upper triangular indices for accesses to G - ! | (1,1) (1,2) (1,3) | = | 1 2 4 | - ! | (2,2) (2,3) | | 3 5 | - ! | (3,3) | | 6 | - ! G is symmetric, compute only the upper triangular part of G=dx^Tdx -!DIR$ LOOP COUNT MAX=3 - DO j=1,dim -!DIR$ LOOP COUNT MAX=3 - DO i=1,j -!DIR$ INLINE - utind = GetSymmetricIndex(i,j) - SELECT CASE (cdim) - CASE(1) - !_ELMER_OMP_SIMD - DO l=1,nc - G(l,utind)=dx(l,1,i)*dx(l,1,j) - END DO - CASE(2) - !_ELMER_OMP_SIMD - DO l=1,nc - G(l,utind)=dx(l,1,i)*dx(l,1,j)+dx(l,2,i)*dx(l,2,j) - END DO - CASE(3) - !_ELMER_OMP_SIMD - DO l=1,nc - G(l,utind)=dx(l,1,i)*dx(l,1,j)+dx(l,2,i)*dx(l,2,j)+dx(l,3,i)*dx(l,3,j) - END DO - END SELECT - END DO - END DO - - !------------------------------------------------------------------------------ - ! Convert the metric to contravariant base, and compute the SQRT(DetG) - !------------------------------------------------------------------------------ - SELECT CASE( dim ) - !------------------------------------------------------------------------------ - ! Line elements - !------------------------------------------------------------------------------ - CASE (1) - ! Determinants - ! DetJ(1:nc) = G(1:nc,1,1) - DetJ(1:nc) = G(1:nc,1) - - DO i=1,nc - IF (DetJ(i) <= TINY(REAL(1,dp))) THEN - AllSuccess = .FALSE. - EXIT - END IF - END DO - - IF (AllSuccess) THEN - !_ELMER_OMP_SIMD - DO i=1,nc - ! Metric(i,1,1) = REAL(1,dp)/DetJ(i) - Metric(i,1) = REAL(1,dp)/DetJ(i) - END DO - !_ELMER_OMP_SIMD - DO i=1,nc - DetJ(i) = SQRT( DetJ(i)) - END DO - END IF - - - !------------------------------------------------------------------------------ - ! Surface elements - !------------------------------------------------------------------------------ - CASE (2) - ! Determinants - !_ELMER_OMP_SIMD - DO i=1,nc - ! DetJ(i) = ( G(i,1,1)*G(i,2,2) - G(i,1,2)*G(i,2,1) ) - ! G is symmetric - DetJ(i) = G(i,1)*G(i,3)-G(i,2)*G(i,2) - END DO - - DO i=1,nc - IF (DetJ(i) <= TINY(REAL(1,dp))) THEN - AllSuccess = .FALSE. - EXIT - END IF - END DO - - IF (AllSuccess) THEN - ! Since G=G^T, it holds G^{-1}=(G^T)^{-1} - !_ELMER_OMP_SIMD - DO i=1,nc - s = REAL(1,dp)/DetJ(i) - ! G is symmetric - ! All in one go, with redundancies eliminated - Metric(i,1) = s*G(i,3) - Metric(i,2) = -s*G(i,2) - Metric(i,3) = s*G(i,1) - END DO - !_ELMER_OMP_SIMD - DO i=1,nc - DetJ(i) = SQRT(DetJ(i)) - END DO - - END IF - !------------------------------------------------------------------------------ - ! Volume elements - !------------------------------------------------------------------------------ - CASE (3) - ! Determinants - !_ELMER_OMP_SIMD - DO i=1,nc - ! DetJ(i) = G(i,1,1) * ( G(i,2,2)*G(i,3,3) - G(i,2,3)*G(i,3,2) ) + & - ! G(i,1,2) * ( G(i,2,3)*G(i,3,1) - G(i,2,1)*G(i,3,3) ) + & - ! G(i,1,3) * ( G(i,2,1)*G(i,3,2) - G(i,2,2)*G(i,3,1) ) - ! G is symmetric - DetJ(i) = G(i,1)*(G(i,3)*G(i,6)-G(i,5)*G(i,5)) + & - G(i,2)*(G(i,5)*G(i,4)-G(i,2)*G(i,6)) + & - G(i,4)*(G(i,2)*G(i,5)-G(i,3)*G(i,4)) - END DO - - DO i=1,nc - IF (DetJ(i) <= TINY(REAL(1,dp))) THEN - AllSuccess = .FALSE. - EXIT - END IF - END DO - - IF (AllSuccess) THEN - ! Since G=G^T, it holds G^{-1}=(G^T)^{-1} - !_ELMER_OMP_SIMD - DO i=1,nc - s = REAL(1,dp) / DetJ(i) - ! Metric(i,1,1) = s * (G(i,2,2)*G(i,3,3) - G(i,3,2)*G(i,2,3)) - ! Metric(i,2,1) = -s * (G(i,2,1)*G(i,3,3) - G(i,3,1)*G(i,2,3)) - ! Metric(i,3,1) = s * (G(i,2,1)*G(i,3,2) - G(i,3,1)*G(i,2,2)) - ! G is symmetric - - ! All in one go, with redundancies eliminated - Metric(i,1)= s*(G(i,3)*G(i,6)-G(i,5)*G(i,5)) - Metric(i,2)=-s*(G(i,2)*G(i,6)-G(i,4)*G(i,5)) - Metric(i,3)= s*(G(i,1)*G(i,6)-G(i,4)*G(i,4)) - Metric(i,4)= s*(G(i,2)*G(i,5)-G(i,3)*G(i,4)) - Metric(i,5)=-s*(G(i,1)*G(i,5)-G(i,2)*G(i,4)) - Metric(i,6)= s*(G(i,1)*G(i,3)-G(i,2)*G(i,2)) - END DO - - !_ELMER_OMP_SIMD - DO i=1,nc - DetJ(i) = SQRT(DetJ(i)) - END DO - - END IF - END SELECT - - IF (AllSuccess) THEN - SELECT CASE(dim) - CASE(1) -!DIR$ LOOP COUNT MAX=3 - DO i=1,cdim - !_ELMER_OMP_SIMD - DO l=1,nc - LtoGMap(l,i,1) = dx(l,i,1)*Metric(l,1) - END DO - END DO - CASE(2) -!DIR$ LOOP COUNT MAX=3 - DO i=1,cdim - !_ELMER_OMP_SIMD - DO l=1,nc - LtoGMap(l,i,1) = dx(l,i,1)*Metric(l,1) + dx(l,i,2)*Metric(l,2) - LtoGMap(l,i,2) = dx(l,i,1)*Metric(l,2) + dx(l,i,2)*Metric(l,3) - END DO - END DO - CASE(3) -!DIR$ LOOP COUNT MAX=3 - DO i=1,cdim - !_ELMER_OMP_SIMD - DO l=1,nc - LtoGMap(l,i,1) = dx(l,i,1)*Metric(l,1) + dx(l,i,2)*Metric(l,2) + dx(l,i,3)*Metric(l,4) - LtoGMap(l,i,2) = dx(l,i,1)*Metric(l,2) + dx(l,i,2)*Metric(l,3) + dx(l,i,3)*Metric(l,5) - LtoGMap(l,i,3) = dx(l,i,1)*Metric(l,4) + dx(l,i,2)*Metric(l,5) + dx(l,i,3)*Metric(l,6) - END DO - END DO - END SELECT - ELSE - - ! Degenerate element! - WRITE( Message,'(A,I0,A,I0,A,I0)') 'Degenerate ',dim,'D element: ',Elm % ElementIndex, ', pt=', i - CALL Error( 'ElementMetricVec', Message ) - WRITE( Message,'(A,G10.3)') 'DetG:',DetJ(i) - CALL Info( 'ElementMetricVec', Message, Level=3 ) - DO i=1,cdim - WRITE( Message,'(A,I0,A,3G10.3)') 'Dir: ',i,' Coord:',Nodes % xyz(i,1),& - Nodes % xyz(i,2), Nodes % xyz(i,3) - CALL Info( 'ElementMetricVec', Message, Level=3 ) - END DO - IF (cdim < dim) THEN - WRITE( Message,'(A,I0,A,I0)') 'Element dim larger than meshdim: ',dim,' vs. ',cdim - CALL Info( 'ElementMetricVec', Message, Level=3 ) - END IF - END IF - - CONTAINS - - FUNCTION GetSymmetricIndex(i,j) RESULT(utind) - IMPLICIT NONE - INTEGER, INTENT(IN) :: i, j - INTEGER :: utind - - IF (i>j) THEN - utind = i*(i-1)/2+j - ELSE - utind = j*(j-1)/2+i - END IF - END FUNCTION GetSymmetricIndex -!------------------------------------------------------------------------------ - END FUNCTION ElementMetricVec -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivatives with -!> respect to global coordinates of a quantity x given at element nodes at -!> local coordinate point u,v,w inside the element. Element basis functions -!> are used to compute the value. This is internal version,and shoudnt -!> usually be called directly by the user, but through the wrapper routine -!> GlobalFirstDerivatives. -!------------------------------------------------------------------------------ - SUBROUTINE GlobalFirstDerivativesInternal( elm,nodes,df,gx,gy,gz, & - Metric,dLBasisdx ) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! Type(Nodes_t) :: nodes -! INPUT: element nodal coordinate arrays -! -! REAL(KIND=dp) :: f(:) -! INPUT: Nodal values of the quantity whose partial derivative we want to know -! -! REAL(KIND=dp) :: gx = @f(u,v)/@x, gy = @f(u,v)/@y, gz = @f(u,v)/@z -! OUTPUT: Values of the partial derivatives -! -! REAL(KIND=dp) :: Metric(:,:) -! INPUT: Contravariant metric tensor of the element coordinate system -! -! REAL(KIND=dp), OPTIONAL :: dLBasisdx(:,:) -! INPUT: Values of partial derivatives with respect to local coordinates -! -! FUNCTION VALUE: -! .TRUE. if element is ok, .FALSE. if degenerated -! -!------------------------------------------------------------------------------ - ! - ! Return value of first derivatives of a quantity f in global - ! coordinates at point (u,v) in gx,gy and gz. - ! - TYPE(Element_t) :: elm - TYPE(Nodes_t) :: nodes - - REAL(KIND=dp) :: df(:),Metric(:,:) - REAL(KIND=dp) :: gx,gy,gz - REAL(KIND=dp) :: dLBasisdx(:,:) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - - REAL(KIND=dp), DIMENSION(:), POINTER :: x,y,z - REAL(KIND=dp) :: dx(3,3),dfc(3),s - - INTEGER :: cdim,dim,i,j,n,NB -!------------------------------------------------------------------------------ - - n = elm % TYPE % NumberOfNodes - dim = elm % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - - x => nodes % x - y => nodes % y - z => nodes % z -!------------------------------------------------------------------------------ -! Partial derivatives of global coordinates with respect to local, and -! partial derivatives of the quantity given, also with respect to local -! coordinates -!------------------------------------------------------------------------------ - SELECT CASE(cdim) - CASE(1) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dLBasisdx(1:n,i) ) - END DO - - CASE(2) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dLBasisdx(1:n,i) ) - dx(2,i) = SUM( y(1:n)*dLBasisdx(1:n,i) ) - END DO - - CASE(3) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dLBasisdx(1:n,i) ) - dx(2,i) = SUM( y(1:n)*dLBasisdx(1:n,i) ) - dx(3,i) = SUM( z(1:n)*dLBasisdx(1:n,i) ) - END DO - END SELECT -!------------------------------------------------------------------------------ -! Contravariant components of partials in element coordinates -!------------------------------------------------------------------------------ - DO i=1,dim - s = 0.0d0 - DO j=1,dim - s = s + Metric(i,j) * df(j) - END DO - dfc(i) = s - END DO -!------------------------------------------------------------------------------ -! Transform partials to space coordinates -!------------------------------------------------------------------------------ - gx = 0.0d0 - gy = 0.0d0 - gz = 0.0d0 - SELECT CASE(cdim) - CASE(1) - gx = SUM( dx(1,1:dim) * dfc(1:dim) ) - - CASE(2) - gx = SUM( dx(1,1:dim) * dfc(1:dim) ) - gy = SUM( dx(2,1:dim) * dfc(1:dim) ) - - CASE(3) - gx = SUM( dx(1,1:dim) * dfc(1:dim) ) - gy = SUM( dx(2,1:dim) * dfc(1:dim) ) - gz = SUM( dx(3,1:dim) * dfc(1:dim) ) - END SELECT - - END SUBROUTINE GlobalFirstDerivativesInternal -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of the first partial derivative with -!> respect to global coordinates of a quantity f given at element nodes at -!> local coordinate point u,v,w inside the element. Element basis functions -!> are used to compute the value. -!------------------------------------------------------------------------------ - SUBROUTINE GlobalFirstDerivatives( Elm, Nodes, df, gx, gy, gz, & - Metric, dLBasisdx ) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! Type(Nodes_t) :: nodes -! INPUT: element nodal coordinate arrays -! -! REAL(KIND=dp) :: f(:) -! INPUT: Nodal values of the quantity whose partial derivatives we want -! to know -! -! REAL(KIND=dp) :: gx=@f(u,v,w)/@x, gy=@f(u,v,w)/@y, gz=@f(u,v,w)/@z -! OUTPUT: Values of the partial derivatives -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the partial derivative -! -! REAL(KIND=dp)L :: dLBasisdx(:,:) -! INPUT: Values of partial derivatives of basis functions with respect to -! local coordinates -! -! REAL(KIND=dp), OPTIONAL :: dBasisdx(:,:) -! INPUT: Values of partial derivatives of basis functions with respect to -! global coordinates can be given here, if known, otherwise they -! will be computed from the element basis functions. -! -!------------------------------------------------------------------------------ - - TYPE(Element_t) :: elm - TYPE(Nodes_t) :: nodes - - REAL(KIND=dp) :: gx,gy,gz - REAL(KIND=dp) :: dLBasisdx(:,:),Metric(:,:),df(:) - -! Local variables -!------------------------------------------------------------------------------ - INTEGER :: n -!------------------------------------------------------------------------------ - - CALL GlobalFirstDerivativesInternal( Elm, Nodes, df, & - gx, gy, gz, Metric, dLBasisdx ) - - END SUBROUTINE GlobalFirstDerivatives -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Given element structure return value of a quantity x given at element nodes -!> at local coordinate point u inside the element. Element basis functions are -!> used to compute the value. This is just a wrapper routine and will call the -!> real function according to element dimension. -!------------------------------------------------------------------------------ - FUNCTION InterpolateInElement( elm,f,u,v,w,Basis ) RESULT(VALUE) -!------------------------------------------------------------------------------ -! -! DESCRIPTION: -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! REAL(KIND=dp) :: f(:) -! INPUT: Nodal values of the quantity whose value we want to know -! -! REAL(KIND=dp) :: u,v,w -! INPUT: Point at which to evaluate the value -! -! REAL(KIND=dp), OPTIONAL :: Basis(:) -! INPUT: Values of the basis functions at the point u,v,w can be given here, -! if known, otherwise the will be computed from the definition -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: y -! value of the quantity y = x(u,v,w) -! -!------------------------------------------------------------------------------ - - TYPE(Element_t) :: elm - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp) :: f(:) - REAL(KIND=dp), OPTIONAL :: Basis(:) - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: VALUE - INTEGER :: n - - IF ( PRESENT( Basis ) ) THEN -!------------------------------------------------------------------------------ -! Basis function values given, just sum the result ... -!------------------------------------------------------------------------------ - n = elm % TYPE % NumberOfNodes - VALUE = SUM( f(1:n)*Basis(1:n) ) - ELSE -!------------------------------------------------------------------------------ -! ... otherwise compute from the definition. -!------------------------------------------------------------------------------ - SELECT CASE (elm % TYPE % DIMENSION) - CASE (0) - VALUE = f(1) - CASE (1) - VALUE = InterpolateInElement1D( elm,f,u ) - CASE (2) - VALUE = InterpolateInElement2D( elm,f,u,v ) - CASE (3) - VALUE = InterpolateInElement3D( elm,f,u,v,w ) - END SELECT - END IF - - END FUNCTION InterpolateInElement -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Compute elementwise matrix of second partial derivatives -!> at given point u,v,w in global coordinates. -!------------------------------------------------------------------------------ - SUBROUTINE GlobalSecondDerivatives(elm,nodes,f,values,u,v,w,Metric,dBasisdx) -!------------------------------------------------------------------------------ -! -! Parameters: -! -! Input: (Element_t) structure describing the element -! (Nodes_t) element nodal coordinates -! (double precision) F nodal values of the quantity -! (double precision) u,v point at which to evaluate -! -! Output: 3x3 matrix (values) of partial derivatives -! -!------------------------------------------------------------------------------ - - TYPE(Nodes_t) :: nodes - TYPE(Element_t) :: elm - - REAL(KIND=dp) :: u,v,w - REAL(KIND=dp) :: f(:),Metric(:,:) - REAL(KIND=dp) :: values(:,:) - REAL(KIND=dp), OPTIONAL :: dBasisdx(:,:) -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - INTEGER :: i,j,k,l,dim,cdim - - REAL(KIND=dp), DIMENSION(3,3,3) :: C1,C2,ddx - REAL(KIND=dp), DIMENSION(3) :: df - REAL(KIND=dp), DIMENSION(3,3) :: cddf,ddf,dx - - REAL(KIND=dp), DIMENSION(:), POINTER :: x,y,z - REAL(KIND=dp) :: s - - INTEGER :: n -!------------------------------------------------------------------------------ -#if 1 -! -! This is actually not quite correct... -! - IF ( elm % TYPE % BasisFunctionDegree <= 1 ) RETURN -#else -! -! this is ... -! - IF ( elm % TYPE % ElementCode <= 202 .OR. & - elm % TYPE % ElementCode == 303 .OR. & - elm % TYPE % ElementCode == 504 ) RETURN -#endif - - n = elm % TYPE % NumberOfNodes - x => nodes % x - y => nodes % y - z => nodes % z - - dim = elm % TYPE % DIMENSION - cdim = CoordinateSystemDimension() - -!------------------------------------------------------------------------------ -! Partial derivatives of the basis functions are given, just -! sum for the first partial derivatives... -!------------------------------------------------------------------------------ - dx = 0.0d0 - df = 0.0d0 - SELECT CASE( cdim ) - CASE(1) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dBasisdx(1:n,i) ) - df(i) = SUM( f(1:n)*dBasisdx(1:n,i) ) - END DO - - CASE(2) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dBasisdx(1:n,i) ) - dx(2,i) = SUM( y(1:n)*dBasisdx(1:n,i) ) - df(i) = SUM( f(1:n)*dBasisdx(1:n,i) ) - END DO - - CASE(3) - DO i=1,dim - dx(1,i) = SUM( x(1:n)*dBasisdx(1:n,i) ) - dx(2,i) = SUM( y(1:n)*dBasisdx(1:n,i) ) - dx(3,i) = SUM( z(1:n)*dBasisdx(1:n,i) ) - df(i) = SUM( f(1:n)*dBasisdx(1:n,i) ) - END DO - END SELECT -!------------------------------------------------------------------------------ -! Get second partial derivatives with respect to local coordinates -!------------------------------------------------------------------------------ - SELECT CASE( dim ) - CASE(1) -!------------------------------------------------------------------------------ -! Line elements -!------------------------------------------------------------------------------ - ddx(1,1,1) = SecondDerivatives1D( elm,x,u ) - ddx(2,1,1) = SecondDerivatives1D( elm,y,u ) - ddx(3,1,1) = SecondDerivatives1D( elm,z,u ) - - CASE(2) -!------------------------------------------------------------------------------ -! Surface elements -!------------------------------------------------------------------------------ - ddx(1,1:2,1:2) = SecondDerivatives2D( elm,x,u,v ) - ddx(2,1:2,1:2) = SecondDerivatives2D( elm,y,u,v ) - ddx(3,1:2,1:2) = SecondDerivatives2D( elm,z,u,v ) - - CASE(3) -!------------------------------------------------------------------------------ -! Volume elements -!------------------------------------------------------------------------------ - ddx(1,1:3,1:3) = SecondDerivatives3D( elm,x,u,v,w ) - ddx(2,1:3,1:3) = SecondDerivatives3D( elm,y,u,v,w ) - ddx(3,1:3,1:3) = SecondDerivatives3D( elm,z,u,v,w ) - END SELECT -! -!------------------------------------------------------------------------------ -! Christoffel symbols of the second kind of the element coordinate system -!------------------------------------------------------------------------------ - DO i=1,dim - DO j=1,dim - DO k=1,dim - s = 0.0d0 - DO l=1,cdim - s = s + ddx(l,i,j)*dx(l,k) - END DO - C2(i,j,k) = s - END DO - END DO - END DO -!------------------------------------------------------------------------------ -! Christoffel symbols of the first kind -!------------------------------------------------------------------------------ - DO i=1,dim - DO j=1,dim - DO k=1,dim - s = 0.0d0 - DO l=1,dim - s = s + Metric(k,l)*C2(i,j,l) - END DO - C1(i,j,k) = s - END DO - END DO - END DO -!------------------------------------------------------------------------------ -! First add ordinary partials (change of the quantity with coordinates)... -!------------------------------------------------------------------------------ - SELECT CASE(dim) - CASE(1) - ddf(1,1) = SecondDerivatives1D( elm,f,u ) - - CASE(2) - ddf(1:2,1:2) = SecondDerivatives2D( elm,f,u,v ) - - CASE(3) - ddf(1:3,1:3) = SecondDerivatives3D( elm,f,u,v,w ) - END SELECT -!------------------------------------------------------------------------------ -! ... then add change of coordinates -!------------------------------------------------------------------------------ - DO i=1,dim - DO j=1,dim - s = 0.0d0 - DO k=1,dim - s = s - C1(i,j,k)*df(k) - END DO - ddf(i,j) = ddf(i,j) + s - END DO - END DO -!------------------------------------------------------------------------------ -! Convert to contravariant base -!------------------------------------------------------------------------------ - DO i=1,dim - DO j=1,dim - s = 0.0d0 - DO k=1,dim - DO l=1,dim - s = s + Metric(i,k)*Metric(j,l)*ddf(k,l) - END DO - END DO - cddf(i,j) = s - END DO - END DO -!------------------------------------------------------------------------------ -! And finally transform to global coordinates -!------------------------------------------------------------------------------ - Values = 0.0d0 - DO i=1,cdim - DO j=1,cdim - s = 0.0d0 - DO k=1,dim - DO l=1,dim - s = s + dx(i,k)*dx(j,l)*cddf(k,l) - END DO - END DO - Values(i,j) = s - END DO - END DO -!------------------------------------------------------------------------------ - END SUBROUTINE GlobalSecondDerivatives -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ - FUNCTION GetEdgeMap( ElementFamily ) RESULT(EdgeMap) -!------------------------------------------------------------------------------ - INTEGER :: ElementFamily - INTEGER, POINTER :: EdgeMap(:,:) - - INTEGER, TARGET :: Point(1,1) - INTEGER, TARGET :: Line(1,2) - INTEGER, TARGET :: Triangle(3,2) - INTEGER, TARGET :: Quad(4,2) - INTEGER, TARGET :: Tetra(6,2) - INTEGER, TARGET :: Pyramid(8,2) - INTEGER, TARGET :: Wedge(9,2) - INTEGER, TARGET :: Brick(12,2) - - LOGICAL :: Initialized(8) = .FALSE. - - SAVE Line, Triangle, Wedge, Brick, Tetra, Quad, Pyramid, Initialized - - SELECT CASE(ElementFamily) - CASE(1) - EdgeMap => Point - CASE(2) - EdgeMap => Line - CASE(3) - EdgeMap => Triangle - CASE(4) - EdgeMap => Quad - CASE(5) - EdgeMap => Tetra - CASE(6) - EdgeMap => Pyramid - CASE(7) - EdgeMap => Wedge - CASE(8) - EdgeMap => Brick - CASE DEFAULT - WRITE( Message,'(A,I0,A)') 'Element family ',ElementFamily,' is not known!' - CALL Fatal( 'GetEdgeMap', Message ) - END SELECT - - IF ( .NOT. Initialized(ElementFamily) ) THEN - Initialized(ElementFamily) = .TRUE. - SELECT CASE(ElementFamily) - CASE(1) - EdgeMap(1,1) = 1 - - CASE(2) - EdgeMap(1,:) = [ 1,2 ] - - CASE(3) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 3,1 ] - - CASE(4) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 3,4 ] - EdgeMap(4,:) = [ 4,1 ] - - CASE(5) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 3,1 ] - EdgeMap(4,:) = [ 1,4 ] - EdgeMap(5,:) = [ 2,4 ] - EdgeMap(6,:) = [ 3,4 ] - - CASE(6) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 4,3 ] - EdgeMap(4,:) = [ 1,4 ] - EdgeMap(5,:) = [ 1,5 ] - EdgeMap(6,:) = [ 2,5 ] - EdgeMap(7,:) = [ 3,5 ] - EdgeMap(8,:) = [ 4,5 ] - - CASE(7) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 3,1 ] - EdgeMap(4,:) = [ 4,5 ] - EdgeMap(5,:) = [ 5,6 ] - EdgeMap(6,:) = [ 6,4 ] - EdgeMap(7,:) = [ 1,4 ] - EdgeMap(8,:) = [ 2,5 ] - EdgeMap(9,:) = [ 3,6 ] - - CASE(8) - EdgeMap(1,:) = [ 1,2 ] - EdgeMap(2,:) = [ 2,3 ] - EdgeMap(3,:) = [ 4,3 ] - EdgeMap(4,:) = [ 1,4 ] - EdgeMap(5,:) = [ 5,6 ] - EdgeMap(6,:) = [ 6,7 ] - EdgeMap(7,:) = [ 8,7 ] - EdgeMap(8,:) = [ 5,8 ] - EdgeMap(9,:) = [ 1,5 ] - EdgeMap(10,:) = [ 2,6 ] - EdgeMap(11,:) = [ 3,7 ] - EdgeMap(12,:) = [ 4,8 ] - END SELECT - END IF -!------------------------------------------------------------------------------ - END FUNCTION GetEdgeMap -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Figure out element diameter parameter for stablization. -!------------------------------------------------------------------------------ - FUNCTION ElementDiameter( elm, nodes, UseLongEdge ) RESULT(hK) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: element -! INPUT: element structure -! -! Type(Nodes_t) :: nodes -! INPUT: Nodal coordinate arrays of the element -! -! FUNCTION VALUE: -! REAL(KIND=dp) :: hK -! -!------------------------------------------------------------------------------ - TYPE(Element_t) :: elm - TYPE(Nodes_t) :: nodes - LOGICAL, OPTIONAL :: UseLongEdge -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp), DIMENSION(:), POINTER :: X,Y,Z - INTEGER :: i,j,k,Family - INTEGER, POINTER :: EdgeMap(:,:) - REAL(KIND=dp) :: x0,y0,z0,hK,A,S,CX,CY,CZ - REAL(KIND=dp) :: J11,J12,J13,J21,J22,J23,G11,G12,G21,G22 - LOGICAL :: LongEdge=.FALSE. -!------------------------------------------------------------------------------ - - IF(PRESENT(UseLongEdge)) LongEdge = UseLongEdge - - X => Nodes % x - Y => Nodes % y - Z => Nodes % z - - Family = Elm % TYPE % ElementCode / 100 - SELECT CASE( Family ) - - CASE(1) - hK = 0.0d0 - -!------------------------------------------------------------------------------ -! Triangular element -!------------------------------------------------------------------------------ - CASE(3) - J11 = X(2) - X(1) - J12 = Y(2) - Y(1) - J13 = Z(2) - Z(1) - J21 = X(3) - X(1) - J22 = Y(3) - Y(1) - J23 = Z(3) - Z(1) - G11 = J11**2 + J12**2 + J13**2 - G12 = J11*J21 + J12*J22 + J13*J23 - G22 = J21**2 + J22**2 + J23**2 - A = SQRT(G11*G22 - G12**2) / 2.0d0 - - CX = ( X(1) + X(2) + X(3) ) / 3.0d0 - CY = ( Y(1) + Y(2) + Y(3) ) / 3.0d0 - CZ = ( Z(1) + Z(2) + Z(3) ) / 3.0d0 - - s = (X(1)-CX)**2 + (Y(1)-CY)**2 + (Z(1)-CZ)**2 - s = s + (X(2)-CX)**2 + (Y(2)-CY)**2 + (Z(2)-CZ)**2 - s = s + (X(3)-CX)**2 + (Y(3)-CY)**2 + (Z(3)-CZ)**2 - - hK = 16.0d0*A*A / ( 3.0d0 * s ) - -!------------------------------------------------------------------------------ -! Quadrilateral -!------------------------------------------------------------------------------ - CASE(4) - CX = (X(2)-X(1))**2 + (Y(2)-Y(1))**2 + (Z(2)-Z(1))**2 - CY = (X(4)-X(1))**2 + (Y(4)-Y(1))**2 + (Z(4)-Z(1))**2 - hk = 2*CX*CY/(CX+CY) - - CASE DEFAULT - EdgeMap => GetEdgeMap(Family) - - IF(LongEdge) THEN - hK = -1.0 * HUGE(1.0_dp) - ELSE - hK = HUGE(1.0_dp) - END IF - - DO i=1,SIZE(EdgeMap,1) - j=EdgeMap(i,1) - k=EdgeMap(i,2) - x0 = X(j) - X(k) - y0 = Y(j) - Y(k) - z0 = Z(j) - Z(k) - IF(LongEdge) THEN - hk = MAX(hK, x0**2 + y0**2 + z0**2) - ELSE - hk = MIN(hK, x0**2 + y0**2 + z0**2) - END IF - END DO - END SELECT - - hK = SQRT( hK ) -!------------------------------------------------------------------------------ - END FUNCTION ElementDiameter -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Figure out if given point x,y,z is inside a triangle, whose node -!> coordinates are given in nx,ny,nz. Method: Invert the basis -!> functions.... -!------------------------------------------------------------------------------ - FUNCTION TriangleInside( nx,ny,nz,x,y,z ) RESULT(inside) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! REAL(KIND=dp) :: nx(:),ny(:),nz(:) -! INPUT: Node coordinate arrays -! -! REAL(KIND=dp) :: x,y,z -! INPUT: point which to consider -! -! FUNCTION VALUE: -! LOGICAL :: inside -! result of the in/out test -! -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: nx(:),ny(:),nz(:),x,y,z - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - LOGICAL :: inside - - REAL(KIND=dp) :: a00,a01,a10,a11,b00,b01,b10,b11,detA,px,py,u,v -!------------------------------------------------------------------------------ - - inside = .FALSE. - - IF ( MAXVAL(nx) < x .OR. MAXVAL(ny) < y ) RETURN - IF ( MINVAL(nx) > x .OR. MINVAL(ny) > y ) RETURN - - A00 = nx(2) - nx(1) - A01 = nx(3) - nx(1) - A10 = ny(2) - ny(1) - A11 = ny(3) - ny(1) - - detA = A00*A11 - A01*A10 - IF ( ABS(detA) < AEPS ) RETURN - - detA = 1 / detA - - B00 = A11*detA - B01 = -A01*detA - B10 = -A10*detA - B11 = A00*detA - - px = x - nx(1) - py = y - ny(1) - u = 0.0d0 - v = 0.0d0 - - u = B00*px + B01*py - IF ( u < 0.0d0 .OR. u > 1.0d0 ) RETURN - - v = B10*px + B11*py - IF ( v < 0.0d0 .OR. v > 1.0d0 ) RETURN - - inside = (u + v <= 1.0d0) -!------------------------------------------------------------------------------ - END FUNCTION TriangleInside -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Figure out if given point x,y,z is inside a quadrilateral, whose -!> node coordinates are given in nx,ny,nz. Method: Invert the -!> basis functions.... -!------------------------------------------------------------------------------ - FUNCTION QuadInside( nx,ny,nz,x,y,z ) RESULT(inside) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! REAL(KIND=dp) :: nx(:),ny(:),nz(:) -! INPUT: Node coordinate arrays -! -! REAL(KIND=dp) :: x,y,z -! INPUT: point which to consider -! -! FUNCTION VALUE: -! LOGICAL :: inside -! result of the in/out test -! -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: nx(:),ny(:),nz(:),x,y,z -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - LOGICAL :: inside - - REAL(KIND=dp) :: r,a,b,c,d,ax,bx,cx,dx,ay,by,cy,dy,px,py,u,v -!------------------------------------------------------------------------------ - inside = .FALSE. - - IF ( MAXVAL(nx) < x .OR. MAXVAL(ny) < y ) RETURN - IF ( MINVAL(nx) > x .OR. MINVAL(ny) > y ) RETURN - - ax = 0.25*( nx(1) + nx(2) + nx(3) + nx(4) ) - bx = 0.25*( -nx(1) + nx(2) + nx(3) - nx(4) ) - cx = 0.25*( -nx(1) - nx(2) + nx(3) + nx(4) ) - dx = 0.25*( nx(1) - nx(2) + nx(3) - nx(4) ) - - ay = 0.25*( ny(1) + ny(2) + ny(3) + ny(4) ) - by = 0.25*( -ny(1) + ny(2) + ny(3) - ny(4) ) - cy = 0.25*( -ny(1) - ny(2) + ny(3) + ny(4) ) - dy = 0.25*( ny(1) - ny(2) + ny(3) - ny(4) ) - - px = x - ax - py = y - ay - - a = cy*dx - cx*dy - b = bx*cy - by*cx + dy*px - dx*py - c = by*px - bx*py - - u = 0.0d0 - v = 0.0d0 - - IF ( ABS(a) < AEPS ) THEN - r = -c / b - IF ( r < -1.0d0 .OR. r > 1.0d0 ) RETURN - - v = r - u = (px - cx*r)/(bx + dx*r) - inside = (u >= -1.0d0 .AND. u <= 1.0d0) - RETURN - END IF - - d = b*b - 4*a*c - IF ( d < 0.0d0 ) RETURN - - d = SQRT(d) - IF ( b>0 ) THEN - r = -2*c/(b+d) - ELSE - r = (-b+d)/(2*a) - END IF - IF ( r >= -1.0d0 .AND. r <= 1.0d0 ) THEN - v = r - u = (px - cx*r)/(bx + dx*r) - - IF ( u >= -1.0d0 .AND. u <= 1.0d0 ) THEN - inside = .TRUE. - RETURN - END IF - END IF - - IF ( b>0 ) THEN - r = -(b+d)/(2*a) - ELSE - r = 2*c/(-b+d) - END IF - IF ( r >= -1.0d0 .AND. r <= 1.0d0 ) THEN - v = r - u = (px - cx*r)/(bx + dx*r) - inside = u >= -1.0d0 .AND. u <= 1.0d0 - RETURN - END IF -!------------------------------------------------------------------------------ - END FUNCTION QuadInside -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Figure out if given point x,y,z is inside a tetrahedron, whose -!> node coordinates are given in nx,ny,nz. Method: Invert the -!> basis functions.... -!------------------------------------------------------------------------------ - FUNCTION TetraInside( nx,ny,nz,x,y,z ) RESULT(inside) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! REAL(KIND=dp) :: nx(:),ny(:),nz(:) -! INPUT: Node coordinate arrays -! -! REAL(KIND=dp) :: x,y,z -! INPUT: point which to consider -! -! FUNCTION VALUE: -! LOGICAL :: inside -! result of the in/out test -! -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: nx(:),ny(:),nz(:),x,y,z - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: A00,A01,A02,A10,A11,A12,A20,A21,A22,detA - REAL(KIND=dp) :: B00,B01,B02,B10,B11,B12,B20,B21,B22 - - LOGICAL :: inside - - REAL(KIND=dp) :: px,py,pz,u,v,w -!------------------------------------------------------------------------------ - inside = .FALSE. - - IF ( MAXVAL(nx) < x .OR. MAXVAL(ny) < y .OR. MAXVAL(nz) < z ) RETURN - IF ( MINVAL(nx) > x .OR. MINVAL(ny) > y .OR. MINVAL(nz) > z ) RETURN - - A00 = nx(2) - nx(1) - A01 = nx(3) - nx(1) - A02 = nx(4) - nx(1) - - A10 = ny(2) - ny(1) - A11 = ny(3) - ny(1) - A12 = ny(4) - ny(1) - - A20 = nz(2) - nz(1) - A21 = nz(3) - nz(1) - A22 = nz(4) - nz(1) - - detA = A00*(A11*A22 - A12*A21) - detA = detA + A01*(A12*A20 - A10*A22) - detA = detA + A02*(A10*A21 - A11*A20) - IF ( ABS(detA) < AEPS ) RETURN - - detA = 1 / detA - - px = x - nx(1) - py = y - ny(1) - pz = z - nz(1) - - B00 = (A11*A22 - A12*A21)*detA - B01 = (A21*A02 - A01*A22)*detA - B02 = (A01*A12 - A11*A02)*detA - - u = B00*px + B01*py + B02*pz - IF ( u < 0.0d0 .OR. u > 1.0d0 ) RETURN - - - B10 = (A12*A20 - A10*A22)*detA - B11 = (A00*A22 - A20*A02)*detA - B12 = (A10*A02 - A00*A12)*detA - - v = B10*px + B11*py + B12*pz - IF ( v < 0.0d0 .OR. v > 1.0d0 ) RETURN - - - B20 = (A10*A21 - A11*A20)*detA - B21 = (A01*A20 - A00*A21)*detA - B22 = (A00*A11 - A10*A01)*detA - - w = B20*px + B21*py + B22*pz - IF ( w < 0.0d0 .OR. w > 1.0d0 ) RETURN - - inside = (u + v + w) <= 1.0d0 -!------------------------------------------------------------------------------ - END FUNCTION TetraInside -!------------------------------------------------------------------------------ - - - -!------------------------------------------------------------------------------ -!> Figure out if given point x,y,z is inside a brick, whose node coordinates -!> are given in nx,ny,nz. Method: Divide to tetrahedrons. -!------------------------------------------------------------------------------ - FUNCTION BrickInside( nx,ny,nz,x,y,z ) RESULT(inside) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! REAL(KIND=dp) :: nx(:),ny(:),nz(:) -! INPUT: Node coordinate arrays -! -! REAL(KIND=dp) :: x,y,z -! INPUT: point which to consider -! -! FUNCTION VALUE: -! LOGICAL :: inside -! result of the in/out test -! -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: nx(:),ny(:),nz(:),x,y,z - -!------------------------------------------------------------------------------ -! Local variables -!------------------------------------------------------------------------------ - LOGICAL :: inside - - INTEGER :: i,j - REAL(KIND=dp) :: px(4),py(4),pz(4),r,s,t,maxx,minx,maxy,miny,maxz,minz - - INTEGER :: map(3,12) -!------------------------------------------------------------------------------ - map = RESHAPE( [ 0,1,2, 0,2,3, 4,5,6, 4,6,7, 3,2,6, 3,6,7, & - 1,5,6, 1,6,2, 0,4,7, 0,7,3, 0,1,5, 0,5,4 ], [ 3,12 ] ) + 1 - - inside = .FALSE. - - IF ( MAXVAL(nx) < x .OR. MAXVAL(ny) < y .OR. MAXVAL(nz) < z ) RETURN - IF ( MINVAL(nx) > x .OR. MINVAL(ny) > y .OR. MINVAL(nz) > z ) RETURN - - px(1) = 0.125d0 * SUM(nx) - py(1) = 0.125d0 * SUM(ny) - pz(1) = 0.125d0 * SUM(nz) - - DO i=1,12 - px(2:4) = nx(map(1:3,i)) - py(2:4) = ny(map(1:3,i)) - pz(2:4) = nz(map(1:3,i)) - - IF ( TetraInside( px,py,pz,x,y,z ) ) THEN - inside = .TRUE. - RETURN - END IF - END DO -!------------------------------------------------------------------------------ - END FUNCTION BrickInside -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -!> Check if the current element has been defined passive. -!> This is done by inspecting a looking an the values of "varname Passive" -!> in the Body Force section. It is determined to be passive if it has -!> more positive than negative hits in an element. -!------------------------------------------------------------------------------ - FUNCTION CheckPassiveElement( UElement ) RESULT( IsPassive ) - !------------------------------------------------------------------------------ - TYPE(Element_t), OPTIONAL, TARGET :: UElement - LOGICAL :: IsPassive - !------------------------------------------------------------------------------ - TYPE(Element_t), POINTER :: Element - TYPE(Element_t), POINTER :: CurElementTmp - REAL(KIND=dp), ALLOCATABLE :: Passive(:) - INTEGER :: body_id, bf_id, nlen, NbrNodes,PassNodes, LimitNodes - LOGICAL :: Found - CHARACTER(LEN=MAX_NAME_LEN) :: PassName - LOGICAL :: NoPassiveElements = .FALSE. - TYPE(Solver_t), POINTER :: pSolver, PrevSolver => NULL() - - SAVE Passive, NoPassiveElements, PrevSolver, PassName - !$OMP THREADPRIVATE(Passive, NoPassiveElements, PrevSolver, PassName) - !------------------------------------------------------------------------------ - IsPassive = .FALSE. - pSolver => CurrentModel % Solver - - IF( .NOT. ASSOCIATED( pSolver, PrevSolver ) ) THEN - PrevSolver => pSolver - nlen = CurrentModel % Solver % Variable % NameLen - PassName = GetVarName(CurrentModel % Solver % Variable) // ' Passive' - NoPassiveElements = .NOT. ListCheckPresentAnyBodyForce( CurrentModel, PassName ) - END IF - - IF( NoPassiveElements ) RETURN - - IF (PRESENT(UElement)) THEN - Element => UElement - CurElementTmp => CurrentModel % CurrentElement - CurrentModel % CurrentElement => UElement - ELSE -#ifdef _OPENMP - IF (omp_in_parallel()) THEN - CALL Fatal('CheckPassiveElement', & - 'Need an element to update inside a threaded region') - END IF -#endif - Element => CurrentModel % CurrentElement - END IF - - body_id = Element % BodyId - IF ( body_id <= 0 ) RETURN ! body_id == 0 for boundary elements - - bf_id = ListGetInteger( CurrentModel % Bodies(body_id) % Values, & - 'Body Force', Found, minv=1,maxv=CurrentModel % NumberOfBodyForces ) - IF ( .NOT. Found ) RETURN - - IF ( ListCheckPresent(CurrentModel % BodyForces(bf_id) % Values, PassName) ) THEN - NbrNodes = Element % TYPE % NumberOfNodes - IF ( ALLOCATED(Passive) ) THEN - IF ( SIZE(Passive) < NbrNodes ) THEN - DEALLOCATE(Passive) - ALLOCATE( Passive(NbrNodes) ) - END IF - ELSE - ALLOCATE( Passive(NbrNodes) ) - END IF - Passive(1:NbrNodes) = ListGetReal( CurrentModel % BodyForces(bf_id) % Values, & - PassName, NbrNodes, Element % NodeIndexes ) - PassNodes = COUNT(Passive(1:NbrNodes)>0) - - ! Go through the extremum cases first, and if the element is not either fully - ! active or passive, then check for some possible given criteria for determining - ! the element active / passive. - !------------------------------------------------------------------------------ - IF( PassNodes == 0 ) THEN - CONTINUE - ELSE IF( PassNodes == NbrNodes ) THEN - IsPassive = .TRUE. - ELSE - LimitNodes = ListGetInteger( CurrentModel % BodyForces(bf_id) % Values, & - 'Passive Element Min Nodes',Found ) - IF( Found ) THEN - IsPassive = ( PassNodes >= LimitNodes ) - ELSE - LimitNodes = ListGetInteger( CurrentModel % BodyForces(bf_id) % Values, & - 'Active Element Min Nodes',Found ) - IF( Found ) THEN - IsPassive = ( PassNodes > NbrNodes - LimitNodes ) - ELSE - IsPassive = ( 2*PassNodes > NbrNodes ) - END IF - END IF - END IF - END IF - - IF (PRESENT(UElement)) CurrentModel % CurrentElement => CurElementTmp -!------------------------------------------------------------------------------ - END FUNCTION CheckPassiveElement -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -!> Normal will point from more dense material to less dense -!> or outwards, if no elements on the other side. -!------------------------------------------------------------------------------ - SUBROUTINE CheckNormalDirection( Boundary,Normal,x,y,z,turn ) -!------------------------------------------------------------------------------ - - TYPE(Element_t), POINTER :: Boundary - TYPE(Nodes_t) :: Nodes - REAL(KIND=dp) :: Normal(3),x,y,z - LOGICAL, OPTIONAL :: turn -!------------------------------------------------------------------------------ - - TYPE (Element_t), POINTER :: Element,LeftElement,RightElement - - INTEGER :: LMat,RMat,n,k - - REAL(KIND=dp) :: x1,y1,z1 - REAL(KIND=dp), ALLOCATABLE :: nx(:),ny(:),nz(:) - LOGICAL :: LPassive -!------------------------------------------------------------------------------ - - IF(.NOT. ASSOCIATED( Boundary % BoundaryInfo ) ) RETURN - - k = Boundary % BoundaryInfo % OutBody - - LeftElement => Boundary % BoundaryInfo % Left - - Element => Null() - IF ( ASSOCIATED(LeftELement) ) THEN - RightElement => Boundary % BoundaryInfo % Right - IF ( ASSOCIATED( RightElement ) ) THEN ! we have a body-body boundary - IF ( k > 0 ) THEN ! declared outbody - IF ( LeftElement % BodyId == k ) THEN - Element => RightElement - ELSE - Element => LeftElement - END IF - ELSE IF (LeftElement % BodyId > RightElement % BodyId) THEN ! normal pointing into body with lower body ID - Element => LeftElement - ELSE IF (LeftElement % BodyId < RightElement % BodyId) THEN! normal pointing into body with lower body ID - Element => RightElement - ELSE ! active/passive boundary - LPassive = CheckPassiveElement( LeftElement ) - IF (LPassive .NEQV. CheckPassiveElement( RightElement )) THEN - IF(LPassive) THEN - Element => RightElement - ELSE - Element => LeftElement - END IF - END IF - END IF - ELSE ! body-vacuum boundary from left->right - Element => LeftElement - END IF - ELSE! body-vacuum boundary from right->left - Element => Boundary % BoundaryInfo % Right - END IF - - IF ( .NOT. ASSOCIATED(Element) ) RETURN - - n = Element % TYPE % NumberOfNodes - - ALLOCATE( nx(n), ny(n), nz(n) ) - - nx(1:n) = CurrentModel % Nodes % x(Element % NodeIndexes) - ny(1:n) = CurrentModel % Nodes % y(Element % NodeIndexes) - nz(1:n) = CurrentModel % Nodes % z(Element % NodeIndexes) - - SELECT CASE( Element % TYPE % ElementCode / 100 ) - - CASE(2,4,8) - x1 = InterpolateInElement( Element, nx, 0.0d0, 0.0d0, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 0.0d0, 0.0d0, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 0.0d0, 0.0d0, 0.0d0 ) - CASE(3) - x1 = InterpolateInElement( Element, nx, 1.0d0/3, 1.0d0/3, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/3, 1.0d0/3, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/3, 1.0d0/3, 0.0d0 ) - CASE(5) - x1 = InterpolateInElement( Element, nx, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - CASE(6) - x1 = InterpolateInElement( Element, nx, 0.0d0, 0.0d0, 1.0d0/3 ) - y1 = InterpolateInElement( Element, ny, 0.0d0, 0.0d0, 1.0d0/3 ) - z1 = InterpolateInElement( Element, nz, 0.0d0, 0.0d0, 1.0d0/3 ) - CASE(7) - x1 = InterpolateInElement( Element, nx, 1.0d0/3, 1.0d0/3, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/3, 1.0d0/3, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/3, 1.0d0/3, 0.0d0 ) - CASE DEFAULT - CALL Fatal('CheckNormalDirection','Invalid elementcode for parent element!') - - END SELECT - x1 = x1 - x - y1 = y1 - y - z1 = z1 - z - - IF ( PRESENT(turn) ) turn = .FALSE. - IF ( x1*Normal(1) + y1*Normal(2) + z1*Normal(3) > 0 ) THEN - IF ( Element % BodyId /= k ) THEN - Normal = -Normal - IF ( PRESENT(turn) ) turn = .TRUE. - END IF - ELSE IF ( Element % BodyId == k ) THEN - Normal = -Normal - IF ( PRESENT(turn) ) turn = .TRUE. - END IF - DEALLOCATE( nx,ny,nz ) -!------------------------------------------------------------------------------ - END SUBROUTINE CheckNormalDirection -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Normal will point out from the parent. -!------------------------------------------------------------------------------ - SUBROUTINE CheckNormalDirectionParent( Boundary,Normal,x,y,z,Element,turn ) -!------------------------------------------------------------------------------ - - TYPE(Element_t), POINTER :: Boundary - TYPE(Nodes_t) :: Nodes - REAL(KIND=dp) :: Normal(3),x,y,z - TYPE(Element_t), POINTER :: Element - LOGICAL, OPTIONAL :: turn -!------------------------------------------------------------------------------ - INTEGER :: n,k - REAL(KIND=dp) :: x1,y1,z1 - REAL(KIND=dp), ALLOCATABLE :: nx(:),ny(:),nz(:) - LOGICAL :: LPassive -!------------------------------------------------------------------------------ - - IF( PRESENT( turn ) ) turn = .FALSE. - - IF ( .NOT. ASSOCIATED(Element) ) RETURN - - n = Element % TYPE % NumberOfNodes - - ALLOCATE( nx(n), ny(n), nz(n) ) - - nx(1:n) = CurrentModel % Nodes % x(Element % NodeIndexes) - ny(1:n) = CurrentModel % Nodes % y(Element % NodeIndexes) - nz(1:n) = CurrentModel % Nodes % z(Element % NodeIndexes) - - SELECT CASE( Element % TYPE % ElementCode / 100 ) - - CASE(2,4,8) - x1 = InterpolateInElement( Element, nx, 0.0d0, 0.0d0, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 0.0d0, 0.0d0, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 0.0d0, 0.0d0, 0.0d0 ) - CASE(3) - x1 = InterpolateInElement( Element, nx, 1.0d0/3, 1.0d0/3, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/3, 1.0d0/3, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/3, 1.0d0/3, 0.0d0 ) - CASE(5) - x1 = InterpolateInElement( Element, nx, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/4, 1.0d0/4, 1.0d0/4 ) - CASE(6) - x1 = InterpolateInElement( Element, nx, 0.0d0, 0.0d0, 1.0d0/3 ) - y1 = InterpolateInElement( Element, ny, 0.0d0, 0.0d0, 1.0d0/3 ) - z1 = InterpolateInElement( Element, nz, 0.0d0, 0.0d0, 1.0d0/3 ) - CASE(7) - x1 = InterpolateInElement( Element, nx, 1.0d0/3, 1.0d0/3, 0.0d0 ) - y1 = InterpolateInElement( Element, ny, 1.0d0/3, 1.0d0/3, 0.0d0 ) - z1 = InterpolateInElement( Element, nz, 1.0d0/3, 1.0d0/3, 0.0d0 ) - CASE DEFAULT - CALL Fatal('CheckNormalDirection','Invalid elementcode for parent element!') - - END SELECT - - ! Test vector points from surface to center of parent - x1 = x1 - x - y1 = y1 - y - z1 = z1 - z - - ! Swap the sign if the tentative normal points to the center, it should point outward - IF ( x1*Normal(1) + y1*Normal(2) + z1*Normal(3) > 0 ) THEN - Normal = -Normal - IF ( PRESENT(turn) ) turn = .TRUE. - END IF - - DEALLOCATE( nx,ny,nz ) -!------------------------------------------------------------------------------ - END SUBROUTINE CheckNormalDirectionParent -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Gives the normal vector of a boundary element. -!> For noncurved elements the normal vector does not depend on the local coordinate -!> while otherwise it does. There are different uses of the function where some -!> do not have the luxury of knowing the local coordinates and hence the center -!> point is used as default. -!------------------------------------------------------------------------------ - FUNCTION NormalVector( Boundary,BoundaryNodes,u0,v0,Check,Parent) RESULT(Normal) -!------------------------------------------------------------------------------ - TYPE(Element_t), POINTER :: Boundary - TYPE(Nodes_t) :: BoundaryNodes - REAL(KIND=dp), OPTIONAL :: u0,v0 - LOGICAL, OPTIONAL :: Check - TYPE(Element_t), POINTER, OPTIONAL :: Parent - REAL(KIND=dp) :: Normal(3) -!------------------------------------------------------------------------------ - LOGICAL :: CheckBody, CheckParent - TYPE(ElementType_t),POINTER :: elt - REAL(KIND=dp) :: u,v,Auu,Auv,Avu,Avv,detA,x,y,z - REAL(KIND=dp) :: dxdu,dxdv,dydu,dydv,dzdu,dzdv - REAL(KIND=dp), DIMENSION(:), POINTER :: nx,ny,nz - -!------------------------------------------------------------------------------ - - nx => BoundaryNodes % x - ny => BoundaryNodes % y - nz => BoundaryNodes % z - - SELECT CASE ( Boundary % TYPE % DIMENSION ) - - CASE ( 0 ) - Normal(1) = 1.0_dp - Normal(2:3) = 0.0_dp - - CASE ( 1 ) - IF( PRESENT( u0 ) ) THEN - u = u0 - ELSE - u = 0.0_dp - END IF - - dxdu = FirstDerivative1D( Boundary,nx,u ) - dydu = FirstDerivative1D( Boundary,ny,u ) - - detA = dxdu*dxdu + dydu*dydu - IF ( detA <= 0._dp ) THEN - Normal = 0._dp - RETURN - END IF - detA = 1.0_dp / SQRT(detA) - Normal(1) = -dydu * detA - Normal(2) = dxdu * detA - Normal(3) = 0.0d0 - - CASE ( 2 ) - IF( PRESENT( u0 ) ) THEN - u = u0 - v = v0 - ELSE - IF( Boundary % TYPE % ElementCode / 100 == 3 ) THEN - u = 1.0_dp/3 - v = 1.0_dp/3 - ELSE - u = 0.0_dp - v = 0.0_dp - END IF - END IF - - dxdu = FirstDerivativeInU2D( Boundary,nx,u,v ) - dydu = FirstDerivativeInU2D( Boundary,ny,u,v ) - dzdu = FirstDerivativeInU2D( Boundary,nz,u,v ) - - dxdv = FirstDerivativeInV2D( Boundary,nx,u,v ) - dydv = FirstDerivativeInV2D( Boundary,ny,u,v ) - dzdv = FirstDerivativeInV2D( Boundary,nz,u,v ) - - Auu = dxdu*dxdu + dydu*dydu + dzdu*dzdu - Auv = dxdu*dxdv + dydu*dydv + dzdu*dzdv - Avv = dxdv*dxdv + dydv*dydv + dzdv*dzdv - - detA = 1.0d0 / SQRT(Auu*Avv - Auv*Auv) - - Normal(1) = (dydu * dzdv - dydv * dzdu) * detA - Normal(2) = (dxdv * dzdu - dxdu * dzdv) * detA - Normal(3) = (dxdu * dydv - dxdv * dydu) * detA - - CASE DEFAULT - CALL Fatal('NormalVector','Invalid dimension for determining normal!') - - END SELECT - - - CheckParent = .FALSE. - IF( PRESENT( Parent ) ) CheckParent = ASSOCIATED( Parent ) - - CheckBody = .FALSE. - IF ( PRESENT(Check) ) CheckBody = Check - - IF ( .NOT. ( CheckBody .OR. CheckParent ) ) RETURN - - SELECT CASE( Boundary % TYPE % ElementCode / 100 ) - - CASE(1) - x = nx(1) - y = nx(1) - z = nz(1) - - CASE(2,4) - x = InterpolateInElement( Boundary,nx,0.0d0,0.0d0,0.0d0 ) - y = InterpolateInElement( Boundary,ny,0.0d0,0.0d0,0.0d0 ) - z = InterpolateInElement( Boundary,nz,0.0d0,0.0d0,0.0d0 ) - - CASE(3) - x = InterpolateInElement( Boundary,nx,1.0d0/3,1.0d0/3,0.0d0) - y = InterpolateInElement( Boundary,ny,1.0d0/3,1.0d0/3,0.0d0) - z = InterpolateInElement( Boundary,nz,1.0d0/3,1.0d0/3,0.0d0) - END SELECT - - IF( CheckParent ) THEN - CALL CheckNormalDirectionParent( Boundary, Normal, x, y, z, Parent ) - ELSE - CALL CheckNormalDirection( Boundary,Normal,x,y,z ) - END IF - -!------------------------------------------------------------------------------ - END FUNCTION NormalVector -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -!> Returns a point that is most importantly supposed to be on the surface -!> For noncurved elements this may simply be the mean while otherwise -!> there may be a need to find the surface node using the local coordinates. -!> Hence the optional parameters. Typically the NormalVector and SurfaceVector -!> should be defined at the same position. -!------------------------------------------------------------------------------ - FUNCTION SurfaceVector( Boundary,BoundaryNodes,u,v ) RESULT(Surface) -!------------------------------------------------------------------------------ - TYPE(Element_t), POINTER :: Boundary - TYPE(Nodes_t) :: BoundaryNodes - REAL(KIND=dp),OPTIONAL :: u,v - REAL(KIND=dp) :: Surface(3) -!------------------------------------------------------------------------------ - REAL(KIND=dp), DIMENSION(:), POINTER :: nx,ny,nz - INTEGER :: i,n -!------------------------------------------------------------------------------ - - nx => BoundaryNodes % x - ny => BoundaryNodes % y - nz => BoundaryNodes % z - n = Boundary % TYPE % NumberOfNodes - - IF( .NOT. PRESENT( u ) ) THEN - Surface(1) = SUM( nx ) / n - Surface(2) = SUM( ny ) / n - Surface(3) = SUM( nz ) / n - ELSE - IF( Boundary % TYPE % DIMENSION == 1 ) THEN - Surface(1) = InterpolateInElement( Boundary,nx,u,0.0_dp,0.0_dp) - Surface(2) = InterpolateInElement( Boundary,ny,u,0.0_dp,0.0_dp) - Surface(3) = InterpolateInElement( Boundary,nz,u,0.0_dp,0.0_dp) - ELSE - Surface(1) = InterpolateInElement( Boundary,nx,u,v,0.0_dp) - Surface(2) = InterpolateInElement( Boundary,ny,u,v,0.0_dp) - Surface(3) = InterpolateInElement( Boundary,nz,u,v,0.0_dp) - END IF - END IF - -!------------------------------------------------------------------------------ - END FUNCTION SurfaceVector -!------------------------------------------------------------------------------ - - -!--------------------------------------------------------------------------- -!> This subroutine tests where the intersection between the line defined by two -!> points and a plane (or line) defined by a boundary element meet. There is -!> an intersection if ( 0 < Lambda < 1 ). Of all intersections the first one is -!> that with the smallest positive lambda. -!--------------------------------------------------------------------------- - FUNCTION LineFaceIntersection(FaceElement,FaceNodes,& - Rinit,Rfin,u,v) RESULT ( Lambda ) -!--------------------------------------------------------------------------- - TYPE(Nodes_t) :: FaceNodes - TYPE(Element_t), POINTER :: FaceElement - REAL(KIND=dp) :: Rinit(3),Rfin(3) - REAL(KIND=dp),OPTIONAL :: u,v - REAL(KIND=dp) :: Lambda - - REAL (KIND=dp) :: Surface(3),t1(3),t2(3),Normal(3),Rproj - REAL (KIND=dp) :: Lambda0 - INTEGER :: third - - third = 3 - -100 CONTINUE - - ! For higher order elements this may be a necessity - IF( PRESENT( u ) .AND. PRESENT(v) ) THEN - Surface = SurfaceVector( FaceElement, FaceNodes, u, v ) - Normal = NormalVector( FaceElement, FaceNodes, u, v ) - - ELSE IF( FaceElement % TYPE % DIMENSION == 2 ) THEN - ! Any point known to be at the surface, even corner node - Surface(1) = FaceNodes % x(1) - Surface(2) = FaceNodes % y(1) - Surface(3) = FaceNodes % z(1) - - ! Tangent vector, nor normalized to unity! - t1(1) = FaceNodes % x(2) - Surface(1) - t1(2) = FaceNodes % y(2) - Surface(2) - t1(3) = FaceNodes % z(2) - Surface(3) - - t2(1) = FaceNodes % x(third) - Surface(1) - t2(2) = FaceNodes % y(third) - Surface(2) - t2(3) = FaceNodes % z(third) - Surface(3) - - ! Normal vector obtained from the cross product of tangent vectoes - ! This is not normalized to unity as value of lambda does not depend on its magnitude - Normal(1) = t1(2)*t2(3) - t1(3)*t2(2) - Normal(2) = t1(3)*t2(1) - t1(1)*t2(3) - Normal(3) = t1(1)*t2(2) - t1(2)*t2(1) - ELSE - Surface(1) = FaceNodes % x(1) - Surface(2) = FaceNodes % y(1) - Surface(3) = 0.0_dp - - Normal(1) = Surface(2) - FaceNodes % y(2) - Normal(2) = FaceNodes % x(2) - Surface(1) - Normal(3) = 0.0_dp - END IF - - ! Project of the line to the face normal - Rproj = SUM( (Rfin - Rinit) * Normal ) - - IF( ABS( Rproj ) < TINY( Rproj ) ) THEN - ! if the intersection cannot be defined make it an impossible one - Lambda = -HUGE( Lambda ) - ELSE - Lambda = SUM( ( Surface - Rinit ) * Normal ) / Rproj - END IF - - IF( FaceElement % NDofs == 4 ) THEN - IF( third == 3 ) THEN - third = 4 - Lambda0 = Lambda - GOTO 100 - END IF - IF( ABS( Lambda0 ) < ABS( Lambda) ) THEN - Lambda = Lambda0 - END IF - END IF - - - END FUNCTION LineFaceIntersection - - -!--------------------------------------------------------------------------- -!> This subroutine performs a similar test as above using slightly different -!> strategy. -!--------------------------------------------------------------------------- - FUNCTION LineFaceIntersection2(FaceElement,FaceNodes,Rinit,Rfin,Intersect) RESULT ( Lambda ) - - TYPE(Nodes_t) :: FaceNodes - TYPE(Element_t), POINTER :: FaceElement - REAL(KIND=dp) :: Rinit(3), Rfin(3),Lambda - LOGICAL :: Intersect -!---------------------------------------------------------------------------- - REAL (KIND=dp) :: A(3,3),B(3),C(3),Eps,Eps2,Eps3,detA,absA,ds - INTEGER :: split, i, n, notriangles, triangle, ElemDim - - Eps = EPSILON( Eps ) - Eps2 = SQRT(TINY(Eps2)) - Eps3 = 1.0d-12 - Lambda = -HUGE( Lambda ) - Intersect = .FALSE. - ElemDim = FaceElement % TYPE % DIMENSION - - ! Then solve the exact points of intersection from a 3x3 or 2x2 linear system - !-------------------------------------------------------------------------- - IF( ElemDim == 2 ) THEN - n = FaceElement % NDofs - ! In 3D rectangular faces are treated as two triangles - IF( n == 4 .OR. n == 8 .OR. n == 9 ) THEN - notriangles = 2 - ELSE - notriangles = 1 - END IF - - DO triangle=1,notriangles - - A(1:3,1) = Rfin(1:3) - Rinit(1:3) - - IF(triangle == 1) THEN - A(1,2) = FaceNodes % x(1) - FaceNodes % x(2) - A(2,2) = FaceNodes % y(1) - FaceNodes % y(2) - A(3,2) = FaceNodes % z(1) - FaceNodes % z(2) - ELSE - A(1,2) = FaceNodes % x(1) - FaceNodes % x(4) - A(2,2) = FaceNodes % y(1) - FaceNodes % y(4) - A(3,2) = FaceNodes % z(1) - FaceNodes % z(4) - END IF - - A(1,3) = FaceNodes % x(1) - FaceNodes % x(3) - A(2,3) = FaceNodes % y(1) - FaceNodes % y(3) - A(3,3) = FaceNodes % z(1) - FaceNodes % z(3) - - ! Check for linearly dependent vectors - detA = A(1,1)*(A(2,2)*A(3,3)-A(2,3)*A(3,2)) & - - A(1,2)*(A(2,1)*A(3,3)-A(2,3)*A(3,1)) & - + A(1,3)*(A(2,1)*A(3,2)-A(2,2)*A(3,1)) - absA = SUM(ABS(A(1,1:3))) * SUM(ABS(A(2,1:3))) * SUM(ABS(A(3,1:3))) - - IF(ABS(detA) <= eps * absA + Eps2) CYCLE -! print *,'detA',detA - - B(1) = FaceNodes % x(1) - Rinit(1) - B(2) = FaceNodes % y(1) - Rinit(2) - B(3) = FaceNodes % z(1) - Rinit(3) - - CALL InvertMatrix( A,3 ) - C(1:3) = MATMUL( A(1:3,1:3),B(1:3) ) - - IF( ANY(C(2:3) < -Eps3) .OR. ANY(C(2:3) > 1.0_dp + Eps3 ) ) CYCLE - IF( C(2)+C(3) > 1.0_dp + Eps3 ) CYCLE - - ! Relate the point of intersection to local coordinates - !IF(corners < 4) THEN - ! u = C(2) - ! v = C(3) - !ELSE IF(corners == 4 .AND. split == 0) THEN - ! u = 2*(C(2)+C(3))-1 - ! v = 2*C(3)-1 - !ELSE - ! ! For the 2nd split of the rectangle the local coordinates switched - ! v = 2*(C(2)+C(3))-1 - ! u = 2*C(3)-1 - !END IF - - Intersect = .TRUE. - Lambda = C(1) - EXIT - - END DO - ELSE - ! In 2D the intersection is between two lines - - A(1:2,1) = Rfin(1:2) - Rinit(1:2) - A(1,2) = FaceNodes % x(1) - FaceNodes % x(2) - A(2,2) = FaceNodes % y(1) - FaceNodes % y(2) - - detA = A(1,1)*A(2,2)-A(1,2)*A(2,1) - absA = SUM(ABS(A(1,1:2))) * SUM(ABS(A(2,1:2))) - - ! Lines are almost parallel => no intersection possible - IF(ABS(detA) <= eps * absA + Eps2) RETURN - - B(1) = FaceNodes % x(1) - Rinit(1) - B(2) = FaceNodes % y(1) - Rinit(2) - - CALL InvertMatrix( A,2 ) - C(1:2) = MATMUL(A(1:2,1:2),B(1:2)) - - IF(C(2) < -Eps3 .OR. C(2) > 1.0_dp + Eps3 ) RETURN - - Intersect = .TRUE. - Lambda = C(1) - -! u = -1.0d0 + 2.0d0 * C(2) - - END IF - -! IF(.NOT. Inside) RETURN - -! stat = ElementInfo( Element, FaceNodes, U, V, W, SqrtElementMetric, & -! Basis, dBasisdx ) - -! Weights(1:n) = Basis(1:n) -! MaxInd = 1 -! DO i=2,n -! IF(Weights(MaxInd) < Weights(i)) MaxInd = i -! END DO - - END FUNCTION LineFaceIntersection2 - - - -!--------------------------------------------------------------------------- -!> This subroutine computes the signed distance of a point from a surface. -!--------------------------------------------------------------------------- - FUNCTION PointFaceDistance(BoundaryElement,BoundaryNodes,& - Coord,Normal,u0,v0) RESULT ( Dist ) -!--------------------------------------------------------------------------- - TYPE(Nodes_t) :: BoundaryNodes - TYPE(Element_t), POINTER :: BoundaryElement - REAL(KIND=dp) :: Coord(3),Normal(3) - REAL(KIND=dp),OPTIONAL :: u0,v0 - REAL(KIND=dp) :: Dist - - REAL (KIND=dp) :: Surface(3),t1(3),t2(3),u,v - - ! For higher order elements this may be a necessity - IF( PRESENT( u0 ) .AND. PRESENT(v0) ) THEN - u = u0 - v = v0 - Surface = SurfaceVector( BoundaryElement, BoundaryNodes, u, v ) - ELSE - u = 0.0_dp - v = 0.0_dp - - ! Any point known to be at the surface, even corner node - Surface(1) = BoundaryNodes % x(1) - Surface(2) = BoundaryNodes % y(1) - Surface(3) = BoundaryNodes % z(1) - END IF - - Normal = NormalVector( BoundaryElement, BoundaryNodes, u, v, .TRUE. ) - - ! Project of the line to the face normal - Dist = SUM( (Surface - Coord ) * Normal ) -END FUNCTION PointFaceDistance - - - -!------------------------------------------------------------------------------ -!> Convert global coordinates x,y,z inside element to local coordinates -!> u,v,w of the element. -!> @todo Change to support p elements -!------------------------------------------------------------------------------ - SUBROUTINE GlobalToLocal( u,v,w,x,y,z,Element,ElementNodes ) -!------------------------------------------------------------------------------ - TYPE(Nodes_t) :: ElementNodes - REAL(KIND=dp) :: x,y,z,u,v,w - TYPE(Element_t), POINTER :: Element -!------------------------------------------------------------------------------ - INTEGER, PARAMETER :: MaxIter = 50 - INTEGER :: i,n - REAL(KIND=dp) :: r,s,t,delta(3),prevdelta(3),J(3,3),J1(3,2),det,swap,acc,err - LOGICAL :: Converged -!------------------------------------------------------------------------------ - - u = 0._dp - v = 0._dp - w = 0._dp - IF (Element % TYPE % DIMENSION==0) RETURN - - n = Element % TYPE % NumberOfNodes - - ! @todo Not supported yet -! IF (ASSOCIATED(Element % PDefs)) THEN -! CALL Fatal('GlobalToLocal','P elements not supported yet!') -! END IF - acc = EPSILON(1.0_dp) - Converged = .FALSE. - - delta = 0._dp - -!------------------------------------------------------------------------------ - DO i=1,Maxiter -!------------------------------------------------------------------------------ - r = InterpolateInElement(Element,ElementNodes % x(1:n),u,v,w) - x - s = InterpolateInElement(Element,ElementNodes % y(1:n),u,v,w) - y - t = InterpolateInElement(Element,ElementNodes % z(1:n),u,v,w) - z - - err = r**2 + s**2 + t**2 - - IF ( err < acc ) THEN - Converged = .TRUE. - EXIT - END IF - - prevdelta = delta - delta = 0.d0 - - SELECT CASE( Element % TYPE % DIMENSION ) - CASE(1) - - J(1,1) = FirstDerivative1D( Element, ElementNodes % x, u ) - J(2,1) = FirstDerivative1D( Element, ElementNodes % y, u ) - J(3,1) = FirstDerivative1D( Element, ElementNodes % z, u ) - - det = SUM( J(1:3,1)**2 ) - delta(1) = (r*J(1,1)+s*J(2,1)+t*J(3,1))/det - - CASE(2) - - J(1,1) = FirstDerivativeInU2D( Element, ElementNodes % x,u,v ) - J(1,2) = FirstDerivativeInV2D( Element, ElementNodes % x,u,v ) - J(2,1) = FirstDerivativeInU2D( Element, ElementNodes % y,u,v ) - J(2,2) = FirstDerivativeInV2D( Element, ElementNodes % y,u,v ) - - SELECT CASE( CoordinateSystemDimension() ) - CASE(3) - J(3,1) = FirstDerivativeInU2D( Element, ElementNodes % z, u, v ) - J(3,2) = FirstDerivativeInV2D( Element, ElementNodes % z, u, v ) - - delta(1) = r - delta(2) = s - delta(3) = t - delta(1:2) = MATMUL( TRANSPOSE(J(1:3,1:2)), delta ) - r = delta(1) - s = delta(2) - - J(1:2,1:2) = MATMUL( TRANSPOSE(J(1:3,1:2)), J(1:3,1:2) ) - delta(3) = 0.0d0 - END SELECT - - CALL SolveLinSys2x2( J(1:2,1:2), delta(1:2), [ r, s] ) - - CASE(3) - J(1,1) = FirstDerivativeInU3D( Element, ElementNodes % x, u, v, w ) - J(1,2) = FirstDerivativeInV3D( Element, ElementNodes % x, u, v, w ) - J(1,3) = FirstDerivativeInW3D( Element, ElementNodes % x, u, v, w ) - - J(2,1) = FirstDerivativeInU3D( Element, ElementNodes % y, u, v, w ) - J(2,2) = FirstDerivativeInV3D( Element, ElementNodes % y, u, v, w ) - J(2,3) = FirstDerivativeInW3D( Element, ElementNodes % y, u, v, w ) - - J(3,1) = FirstDerivativeInU3D( Element, ElementNodes % z, u, v, w ) - J(3,2) = FirstDerivativeInV3D( Element, ElementNodes % z, u, v, w ) - J(3,3) = FirstDerivativeInW3D( Element, ElementNodes % z, u, v, w ) - - CALL SolveLinSys3x3( J, delta, [ r, s, t ] ) - - END SELECT - - IF( i > 10 ) THEN - ! If the same values is suggested over and over again, then exit - ! This may be a sign that the node is off-plane and cannot be - ! described within the element. - IF( SUM( ABS( delta - prevdelta ) ) < acc ) EXIT - - ! Use sloppier criteria when iteration still unsuccessful - IF( i > 20 ) THEN - IF( SUM( ABS( delta - prevdelta ) ) < SQRT( acc ) ) EXIT - END IF - - ! If the iteration does not proceed try with some relaxation - delta = 0.5_dp * delta - END IF - - u = u - delta(1) - v = v - delta(2) - w = w - delta(3) - - -!------------------------------------------------------------------------------ - END DO -!------------------------------------------------------------------------------ - - IF ( .NOT. Converged ) THEN - IF( err > SQRT( acc ) ) THEN - IF( i > MaxIter ) THEN - CALL Warn( 'GlobalToLocal', 'did not converge.') - PRINT *,'rst',i,r,s,t - PRINT *,'err',err,acc,SQRT(acc) - PRINT *,'delta',delta,prevdelta - PRINT *,'uvw',u,v,w - PRINT *,'code',Element % TYPE % ElementCode - PRINT *,'x:',x,ElementNodes % x(1:n) - PRINT *,'y:',y,ElementNodes % y(1:n) - PRINT *,'z:',z,ElementNodes % z(1:n) - ELSE -! CALL Warn( 'GlobalToLocal', 'Node may be out of element') -! PRINT *,'rst',i,r,s,t,acc - END IF - END IF - END IF -!------------------------------------------------------------------------------ - END SUBROUTINE GlobalToLocal -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ - SUBROUTINE InvertMatrix3x3( G,GI,detG ) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: G(3,3),GI(3,3) - REAL(KIND=dp) :: detG, s -!------------------------------------------------------------------------------ - s = 1.0 / DetG - - GI(1,1) = s * (G(2,2)*G(3,3) - G(3,2)*G(2,3)); - GI(2,1) = -s * (G(2,1)*G(3,3) - G(3,1)*G(2,3)); - GI(3,1) = s * (G(2,1)*G(3,2) - G(3,1)*G(2,2)); - - GI(1,2) = -s * (G(1,2)*G(3,3) - G(3,2)*G(1,3)); - GI(2,2) = s * (G(1,1)*G(3,3) - G(3,1)*G(1,3)); - GI(3,2) = -s * (G(1,1)*G(3,2) - G(3,1)*G(1,2)); - - GI(1,3) = s * (G(1,2)*G(2,3) - G(2,2)*G(1,3)); - GI(2,3) = -s * (G(1,1)*G(2,3) - G(2,1)*G(1,3)); - GI(3,3) = s * (G(1,1)*G(2,2) - G(2,1)*G(1,2)); -!------------------------------------------------------------------------------ - END SUBROUTINE InvertMatrix3x3 -!------------------------------------------------------------------------------ - - -!------------------------------------------------------------------------------ -!> Given element and its face map (for some triangular face of element ), -!> this routine returns global direction of triangle face so that -!> functions are continuous over element boundaries -!------------------------------------------------------------------------------ - FUNCTION getTriangleFaceDirection( Element, FaceMap ) RESULT(globalDir) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: Element -! INPUT: Element to get direction to -! -! INTEGER :: FaceMap(3) -! INPUT: Element triangular face map -! -! FUNCTION VALUE: -! INTEGER :: globalDir(3) -! Global direction of triangular face as local node numbers. -! -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t) :: Element - INTEGER :: i, FaceMap(3), globalDir(3), nodes(3) - - nodes = 0 - - ! Put global nodes of face into sorted order - nodes(1:3) = Element % NodeIndexes( FaceMap ) - CALL sort(3, nodes) - - globalDir = 0 - ! Find local numbers of sorted nodes. These local nodes - ! span continuous functions over element boundaries - DO i=1,Element % TYPE % NumberOfNodes - IF (nodes(1) == Element % NodeIndexes(i)) THEN - globalDir(1) = i - ELSE IF (nodes(2) == Element % NodeIndexes(i)) THEN - globalDir(2) = i - ELSE IF (nodes(3) == Element % NodeIndexes(i)) THEN - globalDir(3) = i - END IF - END DO - END FUNCTION getTriangleFaceDirection - - -!------------------------------------------------------------------------------ -!> Given element and its face map (for some square face of element ), -!> this routine returns global direction of square face so that -!> functions are continuous over element boundaries -!------------------------------------------------------------------------------ - FUNCTION getSquareFaceDirection( Element, FaceMap ) RESULT(globalDir) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! Type(Element_t) :: Element -! INPUT: Element to get direction to -! -! INTEGER :: FaceMap(4) -! INPUT: Element square face map -! -! FUNCTION VALUE: -! INTEGER :: globalDir(3) -! Global direction of square face as local node numbers. -! -!------------------------------------------------------------------------------ - IMPLICIT NONE - - TYPE(Element_t) :: Element - INTEGER :: i, A,B,C,D, FaceMap(4), globalDir(4), nodes(4), minGlobal - - ! Get global nodes - nodes(1:4) = Element % NodeIndexes( FaceMap ) - ! Find min global node - minGlobal = nodes(1) - A = 1 - DO i=2,4 - IF (nodes(i) < minGlobal) THEN - A = i - minGlobal = nodes(i) - END IF - END DO - - ! Now choose node B as the smallest node NEXT to min node - B = MOD(A,4)+1 - C = MOD(A+3,4) - IF (C == 0) C = 4 - D = MOD(A+2,4) - IF (D == 0) D = 4 - IF (nodes(B) > nodes(C)) THEN - i = B - B = C - C = i - END IF - - ! Finally find local numbers of nodes A,B and C. They uniquely - ! define a global face so that basis functions are continuous - ! over element boundaries - globalDir = 0 - DO i=1,Element % TYPE % NumberOfNodes - IF (nodes(A) == Element % NodeIndexes(i)) THEN - globalDir(1) = i - ELSE IF (nodes(B) == Element % NodeIndexes(i)) THEN - globalDir(2) = i - ELSE IF (nodes(C) == Element % NodeIndexes(i)) THEN - globalDir(4) = i - ELSE IF (nodes(D) == Element % NodeIndexes(i)) THEN - globalDir(3) = i - END IF - END DO - END FUNCTION getSquareFaceDirection - - -!------------------------------------------------------------------------------ -!> Function checks if given local numbering of a square face -!> is legal for wedge element -!------------------------------------------------------------------------------ - FUNCTION wedgeOrdering( ordering ) RESULT(retVal) -!------------------------------------------------------------------------------ -! -! ARGUMENTS: -! -! INTEGER :: ordering(4) -! INPUT: Local ordering of a wedge square face -! -! FUNCTION VALUE: -! INTEGER :: retVal -! .TRUE. if given ordering is legal for wedge square face, -! .FALSE. otherwise -! -!------------------------------------------------------------------------------ - IMPLICIT NONE - - INTEGER, DIMENSION(4), INTENT(IN) :: ordering - LOGICAL :: retVal - - retVal = .FALSE. - IF ((ordering(1) >= 1 .AND. ordering(1) <= 3 .AND.& - ordering(2) >= 1 .AND. ordering(2) <= 3) .OR. & - (ordering(1) >= 4 .AND. ordering(1) <= 6 .AND.& - ordering(2) >= 4 .AND. ordering(2) <= 6)) THEN - retVal = .TRUE. - END IF - END FUNCTION wedgeOrdering - - !--------------------------------------------------------- - !> Computes the 3D rotation matrix for a given - !> surface normal vector - !--------------------------------------------------------- - FUNCTION ComputeRotationMatrix(PlaneVector) RESULT ( RotMat ) - - REAL(KIND=dp) :: PlaneVector(3), RotMat(3,3), ex(3), ey(3), ez(3) - INTEGER :: i, MinIndex, MidIndex, MaxIndex - - !Ensure PlaneVector is the unit normal - PlaneVector = PlaneVector / SQRT( SUM(PlaneVector ** 2) ) - - !The new z-axis is normal to the defined surface - ez = PlaneVector - - MaxIndex = MAXLOC(ABS(ez),1) - MinIndex = MINLOC(ABS(ez),1) - - !Special case when calving front perfectly aligned to either - ! x or y axis. In this case, make minindex = 3 (ex points upwards) - IF(ABS(ez(3)) == ABS(ez(2)) .OR. ABS(ez(3)) == ABS(ez(1))) & - MinIndex = 3 - - DO i=1,3 - IF(i == MaxIndex .OR. i == MinIndex) CYCLE - MidIndex = i - END DO - - ex(MinIndex) = 1.0 - ex(MidIndex) = 0.0 - - ex(MaxIndex) = -ez(MinIndex)/ez(MaxIndex) - ex = ex / SQRT( SUM(ex ** 2) ) - - !The new y-axis is orthogonal to new x and z axes - ey = CrossProduct(ez, ex) - ey = ey / SQRT( SUM(ey ** 2) ) !just in case... - - RotMat(1,:) = ex - RotMat(2,:) = ey - RotMat(3,:) = ez - - END FUNCTION ComputeRotationMatrix - -END MODULE ElementDescription - - -!> \} diff --git a/fem/src/ElementUtils.F90 b/fem/src/ElementUtils.F90 index 29599f1b2c..50c5614b12 100644 --- a/fem/src/ElementUtils.F90 +++ b/fem/src/ElementUtils.F90 @@ -2993,7 +2993,7 @@ FUNCTION NormalOfDegenerateElement(Model, Element ) RESULT ( Normal ) en % z( n ), STAT=istat ) IF( istat /= 0 ) THEN - CALL Fatal('ElementCharacteristicLengths','Allocation error for ElementNodes') + CALL Fatal('NormalOfDegenerateElement','Allocation error for ElementNodes') END IF en % x(1:n) = Model % Nodes % x(Element % NodeIndexes) diff --git a/fem/src/ElmerSolver.F90 b/fem/src/ElmerSolver.F90 index 7add6ec42c..3d59916555 100644 --- a/fem/src/ElmerSolver.F90 +++ b/fem/src/ElmerSolver.F90 @@ -118,8 +118,9 @@ SUBROUTINE ElmerSolver(initialize) TYPE(Model_t), POINTER, SAVE :: Control CHARACTER(LEN=MAX_NAME_LEN) :: MeshDir, MeshName LOGICAL :: DoControl, GotParams - INTEGER :: nr + INTEGER :: nr,ni REAL(KIND=dp), ALLOCATABLE :: rpar(:) + INTEGER, ALLOCATABLE :: ipar(:) #ifdef HAVE_TRILINOS INTERFACE @@ -169,9 +170,25 @@ END SUBROUTINE TrilinosCleanup CALL GET_COMMAND_ARGUMENT(i, OptionString) READ( OptionString,*) rpar(j) END DO - CALL Info('MAIN','Read '//TRIM(I2S(nr))//' parameters from command line!') - CALL SetParametersMATC(nr,rpar) + CALL Info('MAIN','Read '//TRIM(I2S(nr))//' real parameters from command line!') + CALL SetRealParametersMATC(nr,rpar) END IF + + IF( OptionString=='-ipar' ) THEN + ! Followed by number of paramters + the parameter values + i = i + 1 + CALL GET_COMMAND_ARGUMENT(i, OptionString) + READ( OptionString,*) ni + ALLOCATE( ipar(nr) ) + DO j=1,ni + i = i + 1 + CALL GET_COMMAND_ARGUMENT(i, OptionString) + READ( OptionString,*) ipar(j) + END DO + CALL Info('MAIN','Read '//TRIM(I2S(ni))//' integer parameters from command line!') + CALL SetIntegerParametersMATC(ni,ipar) + END IF + Silent = Silent .OR. & ( OptionString=='-s' .OR. OptionString=='--silent' ) Version = Version .OR. & @@ -551,6 +568,12 @@ END SUBROUTINE TrilinosCleanup !------------------------------------------------------------------------------ IF ( Initialize /= 1 ) CALL Info( 'ElmerSolver', '*** Elmer Solver: ALL DONE ***',Level=3 ) + ! This may be used to study problems at the finish + IF( ListGetLogical( CurrentModel % Simulation,'Dirty Finish', GotIt ) ) THEN + CALL Info('ElmerSolver','Skipping freeing of the Model structure',Level=4) + RETURN + END IF + IF ( Initialize <= 0 ) CALL FreeModel(CurrentModel) #ifdef HAVE_TRILINOS @@ -2178,6 +2201,10 @@ SUBROUTINE ExecSimulation(TimeIntervals, CoupledMinIter, & END IF IF(GotIt) THEN dt = dtfunc + IF(dt < EPSILON(dt) ) THEN + WRITE(Message,'(A,ES12.3)') 'Timestep smaller than epsilon: ',dt + CALL Fatal('ExecSimulation', Message) + END IF ELSE dt = TimestepSizes(interval,1) END IF diff --git a/fem/src/ListMatrix.F90 b/fem/src/ListMatrix.F90 index c820505a89..82ee7b1083 100644 --- a/fem/src/ListMatrix.F90 +++ b/fem/src/ListMatrix.F90 @@ -230,7 +230,7 @@ SUBROUTINE List_ToCRSMatrix(A) A % ListMatrix => NULL() A % FORMAT = MATRIX_CRS - CALL Info('List_ToCRSMatrix','Matrix format changed from List to CRS', Level=8) + CALL Info('List_ToCRSMatrix','Matrix format changed from List to CRS', Level=7) !------------------------------------------------------------------------------- END SUBROUTINE List_ToCRSMatrix @@ -308,7 +308,7 @@ SUBROUTINE List_ToListMatrix(A,Truncate) IF( ASSOCIATED( A % Cols ) ) DEALLOCATE( A % Cols ) IF( ASSOCIATED( A % Diag ) ) DEALLOCATE( A % Diag ) IF( ASSOCIATED( A % Values ) ) DEALLOCATE( A % Values ) - CALL Info('ListToCRSMatrix','Matrix format changed from CRS to List', Level=7) + CALL Info('List_ToListMatrix','Matrix format changed from CRS to List', Level=7) !------------------------------------------------------------------------------- END SUBROUTINE List_ToListMatrix !------------------------------------------------------------------------------- @@ -772,7 +772,7 @@ SUBROUTINE List_ExchangeRowStructure( List,n1,n2 ) TYPE(ListMatrixEntry_t), POINTER :: CList1, CList2, Lptr IF ( .NOT. ASSOCIATED(List) ) THEN - CALL Warn('List_MoveRow','No List matrix present!') + CALL Warn('List_ExchangeRowStructure','No List matrix present!') RETURN END IF @@ -806,49 +806,17 @@ END SUBROUTINE List_ExchangeRowStructure - !------------------------------------------------------------------------------ - SUBROUTINE List_GlueLocalMatrix( A,N,Dofs,Indexes,LocalMatrix ) +!> Add the entries of a local matrix to a list-format matrix. !------------------------------------------------------------------------------ -!****************************************************************************** -! -! DESCRIPTION: -! Add a set of values (.i.e. element stiffness matrix) to a CRS format -! matrix. For this matrix the entries are ordered so that 1st for one -! dof you got all nodes, and then for second etc. -! -! ARGUMENTS: -! -! TYPE(Matrix_t) :: Lmat -! INOUT: Structure holding matrix, values are affected in the process -! -! INTEGER :: Nrow, Ncol -! INPUT: Number of nodes in element, or other dofs -! -! INTEGER :: row0, col0 -! INPUT: Offset of the matrix resulting from other blocks -! -! INTEGER :: row0, col0 -! INPUT: Offset of the matrix resulting from other blocks -! -! INTEGER :: RowInds, ColInds -! INPUT: Permutation of the rows and column dofs -! -! REAL(KIND=dp) :: LocalMatrix(:,:) -! INPUT: A (Nrow x RowDofs) x ( Ncol x ColDofs) matrix holding the values to be -! added to the CRS format matrix -! -!****************************************************************************** + SUBROUTINE List_GlueLocalMatrix( A,N,Dofs,Indexes,LocalMatrix ) !------------------------------------------------------------------------------ - - REAL(KIND=dp) :: LocalMatrix(:,:) - INTEGER :: N,DOFs, Indexes(:) TYPE(ListMatrix_t), POINTER :: A(:) - + INTEGER :: N,DOFs, Indexes(:) + REAL(KIND=dp) :: LocalMatrix(:,:) !------------------------------------------------------------------------------ ! Local variables !------------------------------------------------------------------------------ - REAL(KIND=dp) :: Value INTEGER :: i,j,k,l,c,Row,Col @@ -870,49 +838,19 @@ SUBROUTINE List_GlueLocalMatrix( A,N,Dofs,Indexes,LocalMatrix ) END SUBROUTINE List_GlueLocalMatrix !------------------------------------------------------------------------------ +!------------------------------------------------------------------------------ +!> Add the entries of a local matrix to a list-format matrix by allowing +!> offsets !------------------------------------------------------------------------------ SUBROUTINE List_GlueLocalSubMatrix( List,row0,col0,Nrow,Ncol, & RowInds,ColInds,RowDofs,ColDofs,LocalMatrix ) !------------------------------------------------------------------------------ -!****************************************************************************** -! -! DESCRIPTION: -! Add a set of values (.i.e. element stiffness matrix) to a CRS format -! matrix. For this matrix the entries are ordered so that 1st for one -! dof you got all nodes, and then for second etc. -! -! ARGUMENTS: -! -! TYPE(Matrix_t) :: Lmat -! INOUT: Structure holding matrix, values are affected in the process -! -! INTEGER :: Nrow, Ncol -! INPUT: Number of nodes in element, or other dofs -! -! INTEGER :: row0, col0 -! INPUT: Offset of the matrix resulting from other blocks -! -! INTEGER :: row0, col0 -! INPUT: Offset of the matrix resulting from other blocks -! -! INTEGER :: RowInds, ColInds -! INPUT: Permutation of the rows and column dofs -! -! REAL(KIND=dp) :: LocalMatrix(:,:) -! INPUT: A (Nrow x RowDofs) x ( Ncol x ColDofs) matrix holding the values to be -! added to the CRS format matrix -! -!****************************************************************************** -!------------------------------------------------------------------------------ - - REAL(KIND=dp) :: LocalMatrix(:,:) - TYPE(ListMatrix_t), POINTER :: List(:) + TYPE(ListMatrix_t), POINTER :: List(:) INTEGER :: Nrow,Ncol,RowDofs,ColDofs,Col0,Row0,RowInds(:),ColInds(:) - + REAL(KIND=dp) :: LocalMatrix(:,:) !------------------------------------------------------------------------------ ! Local variables !------------------------------------------------------------------------------ - REAL(KIND=dp) :: Value INTEGER :: i,j,k,l,c,Row,Col diff --git a/fem/src/MainUtils.F90 b/fem/src/MainUtils.F90 index 58982826c5..2ea8abfe08 100644 --- a/fem/src/MainUtils.F90 +++ b/fem/src/MainUtils.F90 @@ -112,7 +112,7 @@ SUBROUTINE CheckLinearSolverOptions( Solver ) #endif CASE( 'cpardiso') #if !defined(HAVE_CPARDISO) || !defined(HAVE_MKL) - CALL Fatal( 'CheckSolverOptions', ' Cluster Pardiso solver has not been installed.' ) + CALL Fatal( 'CheckLinearSolverOptions', ' Cluster Pardiso solver has not been installed.' ) #endif CASE( 'cholmod','spqr' ) #ifndef HAVE_CHOLMOD @@ -1619,8 +1619,8 @@ SUBROUTINE AddEquationBasics( Solver, Name, Transient ) END IF sec_name = tmpname(1:i1) mask_name = tmpname(i2:i3) - CALL Info('CreateIpPerm','masking with section: '//TRIM(sec_name),Level=12) - CALL Info('CreateIpPerm','masking with keyword: '//TRIM(mask_name),Level=12) + CALL Info('AddEquationBasics','masking with section: '//TRIM(sec_name),Level=12) + CALL Info('AddEquationBasics','masking with keyword: '//TRIM(mask_name),Level=12) GotSecName = .TRUE. ELSE sec_name = 'body force' @@ -2009,7 +2009,7 @@ SUBROUTINE CreateTimeDerivativeVariables( Solver, Var ) IF( DoIt ) THEN IF( .NOT. ASSOCIATED( pVar % PrevValues ) ) THEN - CALL Warn('AddEquationSolution',& + CALL Warn('CreateTimeDerivativeVariables',& 'Transient restart requires PrevValues!') ELSE DO k = 1, SIZE( pVar % PrevValues, 2 ) @@ -2325,10 +2325,10 @@ SUBROUTINE AddEquationSolution(Solver, Transient ) freqv => ListGetConstRealArray( Solver % Values, 'Frequency', Found ) IF(Found ) THEN IF( SIZE( Freqv,1) < n ) THEN - CALL Fatal( 'AddEquation', 'Frequency must be at least same size as > Harmonic System Values <') + CALL Fatal( 'AddEquationSolution', 'Frequency must be at least same size as > Harmonic System Values <') END IF ELSE - CALL Fatal( 'AddEquation', '> Frequency < must be given for harmonic analysis.' ) + CALL Fatal( 'AddEquationSolution', '> Frequency < must be given for harmonic analysis.' ) END IF ELSE n = 1 diff --git a/fem/src/MeshUtils.F90 b/fem/src/MeshUtils.F90 index 855ef88ab4..ceb739d0a5 100644 --- a/fem/src/MeshUtils.F90 +++ b/fem/src/MeshUtils.F90 @@ -1325,7 +1325,7 @@ SUBROUTINE EnlargeParallelInfo( Mesh, DiscontPerm ) ! Create the enlarged set of global nodes indexes ALLOCATE( TmpGlobalDofs(n1), STAT=istat ) - IF (istat /= 0) CALL Fatal('LoadMesh', 'Unable to allocate TmpGlobalDofs array.') + IF (istat /= 0) CALL Fatal('EnlargeParallelInfo', 'Unable to allocate TmpGlobalDofs array.') TmpGlobalDofs = 0 DO i=1,n0 TmpGlobalDofs(i) = Mesh % ParallelInfo % GlobalDofs(i) @@ -1448,7 +1448,7 @@ SUBROUTINE ElmerAsciiMesh(Step, PMesh, MeshNamePar, ThisPe, NumPEs, IsParallel ) i=i-1 END DO BaseNameLen = i - CALL Info('LoadMesh','Base mesh name: '//TRIM(MeshNamePar(1:BaseNameLen))) + CALL Info('ElmerAsciiMesh','Base mesh name: '//TRIM(MeshNamePar(1:BaseNameLen))) END IF @@ -1541,28 +1541,28 @@ SUBROUTINE ReadHeaderFile() OPEN( Unit=FileUnit, File=FileName, STATUS='OLD', IOSTAT = iostat ) IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not open file: '//TRIM(Filename)) + CALL Fatal('ReadHeaderFile','Could not open file: '//TRIM(Filename)) ELSE - CALL Info('LoadMesh','Reading header info from file: '//TRIM(FileName),Level=10) + CALL Info('ReadHeaderFile','Reading header info from file: '//TRIM(FileName),Level=10) END IF READ(FileUnit,*,IOSTAT=iostat) Mesh % NumberOfNodes, & Mesh % NumberOfBulkElements,& Mesh % NumberOfBoundaryElements IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not read header 1st line in file: '//TRIM(FileName)) + CALL Fatal('ReadHeaderFile','Could not read header 1st line in file: '//TRIM(FileName)) END IF Types = 0 CountByType = 0 READ(FileUnit,*,IOSTAT=iostat) TypeCount IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not read the type count in file: '//TRIM(FileName)) + CALL Fatal('ReadHeaderFile','Could not read the type count in file: '//TRIM(FileName)) END IF DO i=1,TypeCount READ(FileUnit,*,IOSTAT=iostat) Types(i),CountByType(i) IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not read type count '& + CALL Fatal('ReadHeaderFile','Could not read type count '& //TRIM(I2S(i))//'in file: '//TRIM(FileName)) END IF END DO @@ -1570,7 +1570,7 @@ SUBROUTINE ReadHeaderFile() IF( Parallel ) THEN READ(FileUnit,*,IOSTAT=iostat) SharedNodes IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not read shared nodes in file: '//TRIM(FileName)) + CALL Fatal('ReadHeaderFile','Could not read shared nodes in file: '//TRIM(FileName)) END IF ELSE SharedNodes = 0 @@ -1605,9 +1605,9 @@ SUBROUTINE ReadNodesFile() OPEN( Unit=FileUnit, File=FileName, STATUS='OLD', IOSTAT = iostat ) IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not open file: '//TRIM(Filename)) + CALL Fatal('ReadNodesFile','Could not open file: '//TRIM(Filename)) ELSE - CALL Info('LoadMesh','Reading nodes from file: '//TRIM(FileName),Level=10) + CALL Info('ReadNodesFile','Reading nodes from file: '//TRIM(FileName),Level=10) END IF ALLOCATE( NodeTags(Mesh % NumberOfNodes ) ) @@ -1617,7 +1617,7 @@ SUBROUTINE ReadNodesFile() DO j = 1, Mesh % NumberOfNodes READ(FileUnit,*,IOSTAT=iostat) NodeTag, k, Coords IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Problem load node '//TRIM(I2S(j))//' in file: '//TRIM(Filename)) + CALL Fatal('ReadNodesFile','Problem load node '//TRIM(I2S(j))//' in file: '//TRIM(Filename)) END IF IF( NodeTags(j) /= j ) NodePermutation = .TRUE. @@ -1643,7 +1643,7 @@ SUBROUTINE ReadElementsFile() LOGICAL :: halo - CALL AllocateVector( ElementTags, Mesh % NumberOfBulkElements+1, 'LoadMesh') + CALL AllocateVector( ElementTags, Mesh % NumberOfBulkElements+1, 'ReadElementsFile') ElementTags = 0 ElementPermutation = .FALSE. @@ -1659,7 +1659,7 @@ SUBROUTINE ReadElementsFile() IF( iostat /= 0 ) THEN CALL Fatal('ReadElementsFile','Could not open file: '//TRIM(Filename)) ELSE - CALL Info('LoadMesh','Reading bulk elements from file: '//TRIM(FileName),Level=10) + CALL Info('ReadElementsFile','Reading bulk elements from file: '//TRIM(FileName),Level=10) END IF @@ -1750,7 +1750,7 @@ SUBROUTINE ReadBoundaryFile() MaxEIndex = MAXVAL( ElementTags(1:Mesh % NumberOfBulkElements) ) LocalEPerm => NULL() - CALL AllocateVector( LocalEPerm, MaxEIndex - MinEIndex + 1, 'LoadMesh' ) + CALL AllocateVector( LocalEPerm, MaxEIndex - MinEIndex + 1, 'ReadBoundaryFile' ) LocalEPerm = 0 DO i=1,Mesh % NumberOfBulkElements LocalEPerm( ElementTags(i) - MinEIndex + 1 ) = i @@ -1765,7 +1765,7 @@ SUBROUTINE ReadBoundaryFile() IF( iostat /= 0 ) THEN CALL Fatal('ReadBoundaryFile','Could not open file: '//TRIM(Filename)) ELSE - CALL Info('LoadMesh','Reading boundary elements from file: '//TRIM(FileName),Level=10) + CALL Info('ReadBoundaryFile','Reading boundary elements from file: '//TRIM(FileName),Level=10) END IF @@ -1774,12 +1774,12 @@ SUBROUTINE ReadBoundaryFile() Element => Mesh % Elements(j) IF(.NOT. ASSOCIATED( Element ) ) THEN - CALL Fatal('ReadElementsFile','Element '//TRIM(I2S(i))//' not associated!') + CALL Fatal('ReadBoundaryFile','Element '//TRIM(I2S(i))//' not associated!') END IF READ(FileUnit, '(a)', IOSTAT=iostat) str IF( iostat /= 0 ) THEN - CALL Fatal('ReadElementsFile','Could not read boundary element entry: '//TRIM(I2S(j))) + CALL Fatal('ReadBoundaryFile','Could not read boundary element entry: '//TRIM(I2S(j))) END IF nread = read_ints(str,ivals,halo) @@ -1877,12 +1877,12 @@ SUBROUTINE PermuteNodeNumbering() TYPE(Element_t), POINTER :: Element IF( NodePermutation ) THEN - CALL Info('LoadMesh','Performing node mapping',Level=6) + CALL Info('PermuteNodeNumbering','Performing node mapping',Level=6) MinNodeTag = MINVAL( NodeTags ) MaxNodeTag = MAXVAL( NodeTags ) - CALL AllocateVector( LocalPerm, MaxNodeTag-MinNodeTag+1, 'LoadMesh' ) + CALL AllocateVector( LocalPerm, MaxNodeTag-MinNodeTag+1, 'PermuteNodeNumbering' ) LocalPerm = 0 DO i=1,Mesh % NumberOfNodes LocalPerm(NodeTags(i) - MinNodeTag + 1) = i @@ -1898,7 +1898,7 @@ SUBROUTINE PermuteNodeNumbering() END DO END DO ELSE - CALL Info('LoadMesh','Node mapping is continuous',Level=8) + CALL Info('PermuteNodeNumbering','Node mapping is continuous',Level=8) END IF ! Set the for now, if the case is truly parallel we'll have to revisit these @@ -1944,13 +1944,13 @@ SUBROUTINE InitParallelInfo() Mesh % ParallelInfo % GlobalDofs => TmpGlobalDofs ALLOCATE(Mesh % ParallelInfo % NeighbourList(n), STAT=istat) - IF (istat /= 0) CALL Fatal('LoadMesh', 'Unable to allocate NeighbourList array.') + IF (istat /= 0) CALL Fatal('InitParallelInfo', 'Unable to allocate NeighbourList array.') DO i=1,n NULLIFY( Mesh % ParallelInfo % NeighbourList(i) % Neighbours ) END DO - CALL AllocateVector( Mesh % ParallelInfo % INTERFACE, n, 'LoadMesh') + CALL AllocateVector( Mesh % ParallelInfo % INTERFACE, n, 'InitParallelInfo') Mesh % ParallelInfo % INTERFACE = .FALSE. END SUBROUTINE InitParallelInfo @@ -1973,9 +1973,9 @@ SUBROUTINE ReadSharedFile() OPEN( Unit=FileUnit, File=FileName, STATUS='OLD', IOSTAT = iostat ) IF( iostat /= 0 ) THEN - CALL Fatal('LoadMesh','Could not open file: '//TRIM(Filename)) + CALL Fatal('ReadSharedFile','Could not open file: '//TRIM(Filename)) ELSE - CALL Info('LoadMesh','Reading nodes from file: '//TRIM(FileName),Level=10) + CALL Info('ReadSharedFile','Reading nodes from file: '//TRIM(FileName),Level=10) END IF ! This loop could be made more effective, for example @@ -1985,7 +1985,7 @@ SUBROUTINE ReadSharedFile() DO i=1,SharedNodes READ(FileUnit, '(a)', IOSTAT=iostat) str IF( iostat /= 0 ) THEN - CALL Fatal('ReadElementsFile','Could not read shared nodes entry: '//TRIM(I2S(i))) + CALL Fatal('ReadSharedFile','Could not read shared nodes entry: '//TRIM(I2S(i))) END IF nread = read_ints(str,ivals,halo) @@ -2247,7 +2247,7 @@ SUBROUTINE MapBodiesAndBCs() END DO IF( Found ) THEN - CALL Info('LoadMesh','Remapping bodies',Level=8) + CALL Info('MapBodiesAndBCs','Remapping bodies',Level=8) minid = HUGE( minid ) maxid = -HUGE( maxid ) DO i=1,Mesh % NumberOfBulkElements @@ -2257,10 +2257,10 @@ SUBROUTINE MapBodiesAndBCs() maxid = MAX( id, maxid ) END DO IF( minid > maxid ) THEN - CALL Fatal('LoadMesh','Body indexes are screwed!') + CALL Fatal('MapBodiesAndBCs','Body indexes are screwed!') END IF - CALL Info('LoadMesh','Minimum initial body index: '//TRIM(I2S(minid)),Level=6 ) - CALL Info('LoadMesh','Maximum initial body index: '//TRIM(I2S(maxid)),Level=6 ) + CALL Info('MapBodiesAndBCs','Minimum initial body index: '//TRIM(I2S(minid)),Level=6 ) + CALL Info('MapBodiesAndBCs','Maximum initial body index: '//TRIM(I2S(maxid)),Level=6 ) minid = MIN( 1, minid ) maxid = MAX( Model % NumberOfBodies, maxid ) @@ -2275,11 +2275,11 @@ SUBROUTINE MapBodiesAndBCs() body = Blist(k) IF( body > maxid .OR. body < minid ) THEN #if 0 - CALL Warn('LoadMesh','Unused body entry in > Target Bodies < : '& + CALL Warn('MapBodiesAndBCs','Unused body entry in > Target Bodies < : '& //TRIM(I2S(body)) ) #endif ELSE IF( IndexMap( body ) /= 0 ) THEN - CALL Warn('LoadMesh','Multiple bodies have same > Target Bodies < entry : '& + CALL Warn('MapBodiesAndBCs','Multiple bodies have same > Target Bodies < entry : '& //TRIM(I2S(body))) ELSE IndexMap( body ) = id @@ -2287,7 +2287,7 @@ SUBROUTINE MapBodiesAndBCs() END DO ELSE IF( IndexMap( id ) /= 0 ) THEN - CALL Warn('LoadMesh','Unset body already set by > Target Boundaries < : '& + CALL Warn('MapBodiesAndBCs','Unset body already set by > Target Boundaries < : '& //TRIM(I2S(id)) ) ELSE IndexMap( id ) = id @@ -2315,7 +2315,7 @@ SUBROUTINE MapBodiesAndBCs() DEALLOCATE( IndexMap ) ELSE - CALL Info('LoadMesh','Skipping remapping of bodies',Level=10) + CALL Info('MapBodiesAndBCs','Skipping remapping of bodies',Level=10) END IF @@ -2323,7 +2323,7 @@ SUBROUTINE MapBodiesAndBCs() ! Target boundaries are usually given so this is not conditional !--------------------------------------------------------------- - CALL Info('LoadMesh','Remapping boundaries',Level=8) + CALL Info('MapBodiesAndBCs','Remapping boundaries',Level=8) minid = HUGE( minid ) maxid = -HUGE( maxid ) DO i=Mesh % NumberOfBulkElements+1,& @@ -2335,10 +2335,10 @@ SUBROUTINE MapBodiesAndBCs() END DO - CALL Info('LoadMesh','Minimum initial boundary index: '//TRIM(I2S(minid)),Level=6 ) - CALL Info('LoadMesh','Maximum initial boundary index: '//TRIM(I2S(maxid)),Level=6 ) + CALL Info('MapBodiesAndBCs','Minimum initial boundary index: '//TRIM(I2S(minid)),Level=6 ) + CALL Info('MapBodiesAndBCs','Maximum initial boundary index: '//TRIM(I2S(maxid)),Level=6 ) IF( minid > maxid ) THEN - CALL Fatal('LoadMesh','Boundary indexes are screwed') + CALL Fatal('MapBodiesAndBCs','Boundary indexes are screwed') END IF minid = MIN( minid, 1 ) @@ -2353,9 +2353,9 @@ SUBROUTINE MapBodiesAndBCs() IF( id == 0 ) CYCLE bndry = Model % BoundaryId(j) IF( bndry > maxid ) THEN - CALL Warn('LoadMesh','BoundaryId exceeds range') + CALL Warn('MapBodiesAndBCs','BoundaryId exceeds range') ELSE IF( bndry == 0 ) THEN - CALL Warn('LoadMesh','BoundaryId is zero') + CALL Warn('MapBodiesAndBCs','BoundaryId is zero') ELSE IndexMap( bndry ) = id END IF @@ -2373,11 +2373,11 @@ SUBROUTINE MapBodiesAndBCs() IF( bndry > maxid ) THEN #if 0 in my opinion, this is quite usual ... Juha - CALL Warn('LoadMesh','Unused BC entry in > Target Boundaries < : '& + CALL Warn('MapBodiesAndBCs','Unused BC entry in > Target Boundaries < : '& //TRIM(I2S(bndry)) ) #endif ELSE IF( IndexMap( bndry ) /= 0 ) THEN - CALL Warn('LoadMesh','Multiple BCs have same > Target Boundaries < entry : '& + CALL Warn('MapBodiesAndBCs','Multiple BCs have same > Target Boundaries < entry : '& //TRIM(I2S(bndry)) ) ELSE IndexMap( bndry ) = id @@ -2388,7 +2388,7 @@ SUBROUTINE MapBodiesAndBCs() ListCheckPresent(Model % BCs(id) % Values, 'Target Coordinates')) & CYCLE IF (IndexMap( id ) /= 0 .AND. id == DefaultTargetBC ) THEN ! DefaultTarget has been given - CALL Warn('LoadMesh','Default Target is a Target Boundaries entry in > Boundary Condition < : '& + CALL Warn('MapBodiesAndBCs','Default Target is a Target Boundaries entry in > Boundary Condition < : '& //TRIM(I2S(IndexMap(id))) ) END IF ! @@ -2409,7 +2409,7 @@ SUBROUTINE MapBodiesAndBCs() END IF IF( DefaultTargetBC /= 0 ) THEN - CALL Info('LoadMesh','Default Target BC: '& + CALL Info('MapBodiesAndBCs','Default Target BC: '& //TRIM(I2S(DefaultTargetBC)),Level=8) END IF @@ -2423,7 +2423,7 @@ SUBROUTINE MapBodiesAndBCs() bndry = Element % BoundaryInfo % Constraint IF( bndry > maxid .OR. bndry < minid ) THEN - CALL Warn('LoadMesh','Boundary index '//TRIM(I2S(bndry))& + CALL Warn('MapBodiesAndBCs','Boundary index '//TRIM(I2S(bndry))& //' not in range: '//TRIM(I2S(minid))//','//TRIM(I2S(maxid)) ) END IF @@ -2482,20 +2482,20 @@ SUBROUTINE MapCoordinates() CoordMap => ListGetIntegerArray( Model % Simulation, & 'Coordinate Mapping',GotIt ) IF ( GotIt ) THEN - CALL Info('LoadMesh','Performing coordinate mapping',Level=8) + CALL Info('MapCoordinates','Performing coordinate mapping',Level=8) IF ( SIZE( CoordMap ) /= 3 ) THEN WRITE( Message, * ) 'Inconsistent Coordinate Mapping: ', CoordMap - CALL Error( 'LoadMesh', Message ) + CALL Error( 'MapCoordinates', Message ) WRITE( Message, * ) 'Coordinate mapping should be a permutation of 1,2 and 3' - CALL Fatal( 'LoadMesh', Message ) + CALL Fatal( 'MapCoordinates', Message ) END IF IF ( ALL( CoordMap(1:3) /= 1 ) .OR. ALL( CoordMap(1:3) /= 2 ) .OR. ALL( CoordMap(1:3) /= 3 ) ) THEN WRITE( Message, * ) 'Inconsistent Coordinate Mapping: ', CoordMap - CALL Error( 'LoadMesh', Message ) + CALL Error( 'MapCoordinates', Message ) WRITE( Message, * ) 'Coordinate mapping should be a permutation of 1,2 and 3' - CALL Fatal( 'LoadMesh', Message ) + CALL Fatal( 'MapCoordinates', Message ) END IF IF( CoordMap(1) == 1 ) THEN @@ -2543,7 +2543,7 @@ SUBROUTINE MapCoordinates() CoordScale(i) = Wrk(j,1) END DO WRITE(Message,'(A,3ES10.3)') 'Scaling coordinates:',CoordScale(1:3) - CALL Info('LoadMesh',Message) + CALL Info('MapCoordinates',Message) Mesh % Nodes % x = CoordScale(1) * Mesh % Nodes % x IF( mesh_dim > 1 ) Mesh % Nodes % y = CoordScale(2) * Mesh % Nodes % y IF( mesh_dim > 2 ) Mesh % Nodes % z = CoordScale(3) * Mesh % Nodes % z @@ -3426,7 +3426,7 @@ SUBROUTINE ReadElementPropertyFile(FileName,Mesh) DO WHILE( ReadAndTrim(FileUnit,str) ) READ( str(9:),*) i IF ( i < 0 .OR. i > Mesh % NumberOFBulkElements ) THEN - CALL Fatal( 'ReadElementProperties', 'Element id out of range.' ) + CALL Fatal( 'ReadElementPropertyFile', 'Element id out of range.' ) END IF IF ( SEQL( str, 'element:') ) THEN @@ -3814,7 +3814,7 @@ SUBROUTINE InspectQuadraticMesh( Mesh, EnforceToCenter ) CenterMap => BrickCenterMap CASE DEFAULT - CALL Fatal('FindMeshEdges','Element type '//TRIM(I2S(ElemCode))//' not implemented!') + CALL Fatal('InspectQuadraticMesh','Element type '//TRIM(I2S(ElemCode))//' not implemented!') END SELECT @@ -3927,8 +3927,8 @@ SUBROUTINE DetectMortarPairs( Model, Mesh, Tol, BCMode, SameCoordinate ) MaxBC = MAX( MaxBC, BC ) END DO - CALL Info('DetectMortarParis','Minimum Constraint index: '//TRIM(I2S(MinBC)),Level=8) - CALL Info('DetectMortarParis','Maximum Constraint index: '//TRIM(I2S(MaxBC)),Level=8) + CALL Info('DetectMortarPairs','Minimum Constraint index: '//TRIM(I2S(MinBC)),Level=8) + CALL Info('DetectMortarPairs','Maximum Constraint index: '//TRIM(I2S(MaxBC)),Level=8) IF( MaxBC - MinBC < 1 ) THEN CALL Warn('DetectMortarPairs','Needs at least two different BC indexes to create mortar pair!') RETURN @@ -4970,7 +4970,7 @@ SUBROUTINE PreRotationalProjector(BMesh1, BMesh2, MirrorNode ) DO i=-SectorMax,SectorMax IF( SectorCount(i) > 0 ) THEN WRITE( Message,'(A,I0,A,I0)') 'Sector:',i,' Nodes:',SectorCount(i) - CALL Info('MatchInterfaceNodes',Message,Level=8) + CALL Info('PreRotationalProjector',Message,Level=8) END IF END DO IF( AntiPeriodic ) THEN @@ -5078,7 +5078,7 @@ SUBROUTINE TemporalTriangleMortarAssembly(ElementT, NodesT, Element, Nodes, Elem IF(.NOT. AllocationsDone ) THEN n = CurrentModel % Mesh % MaxElementNodes ALLOCATE( BasisT(3),Basis(n), BasisM(n), CoeffBasis(n), MASS(n,n), STAT = AllocStat ) - IF( AllocStat /= 0 ) CALL Fatal('LocalMortarAssembly','Allocation error!') + IF( AllocStat /= 0 ) CALL Fatal('TemporalTriangleMortarAssembly','Allocation error!') AllocationsDone = .TRUE. END IF @@ -5278,7 +5278,7 @@ FUNCTION NormalProjector(BMesh2, BMesh1, BC) RESULT ( Projector ) TYPE(Element_t), POINTER :: Element INTEGER :: i,n,m - CALL Info('NormalProjector3D','Creating projector between 3D surfaces',Level=7) + CALL Info('NormalProjector','Creating projector between 3D surfaces',Level=7) Parallel = ( ParEnv % PEs > 1 ) Mesh => CurrentModel % Mesh @@ -5310,7 +5310,7 @@ FUNCTION NormalProjector(BMesh2, BMesh1, BC) RESULT ( Projector ) BiOrthogonalBasis = ListGetLogical( CurrentModel % Solver % Values, & 'Eliminate Linear Constraints',Found ) IF( BiOrthogonalBasis ) THEN - CALL Info('LevelProjector',& + CALL Info('NormalProjector',& 'Enforcing > Use Biorthogonal Basis < to True to enable elimination',Level=8) CALL ListAddLogical( BC, 'Use Biorthogonal Basis',.TRUE. ) END IF @@ -5336,7 +5336,7 @@ FUNCTION NormalProjector(BMesh2, BMesh1, BC) RESULT ( Projector ) Projector % Child => AllocateMatrix() Projector % Child % Format = MATRIX_LIST - CALL Info('LevelProjector','Using biorthogonal basis, as requested',Level=8) + CALL Info('NormalProjector','Using biorthogonal basis, as requested',Level=8) END IF @@ -6494,7 +6494,7 @@ SUBROUTINE CreateEdgeCenters( Mesh, EdgeMesh, noedges, EdgeInds, EdgeX, EdgeY ) END DO IF(noedges > 0 .AND. .NOT. AllocationsDone ) THEN - CALL Info('ConformingEdgePerm','Allocating stuff for edges',Level=20) + CALL Info('CreateEdgeCenters','Allocating stuff for edges',Level=20) ALLOCATE( EdgeInds(noedges), EdgeX(3,noedges), EdgeY(3,noedges) ) AllocationsDone = .TRUE. GOTO 100 @@ -7258,7 +7258,7 @@ FUNCTION CheckMeshSkew(BMesh, NotAllQuads) RESULT( MaxSkew ) REAL(KIND=dp) :: e1(2),e2(2),DotProdM, PhiM INTEGER, POINTER :: IndexesM(:) - CALL Info('LevelProjector','Checking mesh skew') + CALL Info('CheckMeshSkew','Checking mesh skew') n = 4 ALLOCATE( NodesM % x(n), NodesM % y(n) ) @@ -7328,7 +7328,7 @@ SUBROUTINE AddNodalProjectorStrongStrides() TYPE(Nodes_t) :: NodesM LOGICAL :: LeftCircle - CALL Info('LevelProjector','Creating strong stride projector for nodal dofs',Level=10) + CALL Info('AddNodalProjectorStrongStrides','Creating strong stride projector for nodal dofs',Level=10) n = Mesh % MaxElementNodes ALLOCATE( NodesM % x(n), NodesM % y(n), NodesM % z(n) ) @@ -7439,7 +7439,7 @@ SUBROUTINE AddNodalProjectorStrongStrides() EXIT END IF END DO - IF( j2 == 0 ) CALL Warn('LevelProjector','Could not locate an edge consistently!') + IF( j2 == 0 ) CALL Warn('AddNodalProjectorStrongStrides','Could not locate an edge consistently!') END IF ! The node to map must be in interval, x1 \in [xm1,xm2] @@ -7467,7 +7467,7 @@ SUBROUTINE AddNodalProjectorStrongStrides() ! When we have the correct edge, the mapping is trivial. ! The sum of weights of the projectors is set to one. IF( ABS(xm1-xm2) < TINY(xm1) ) THEN - CALL Warn('LevelProjector','Degenerated edge?') + CALL Warn('AddNodalProjectorStrongStrides','Degenerated edge?') PRINT *,'ind',ind,x1,y1,xm1,xm2,j1,j2,j3 PRINT *,'x:',NodesM % x(1:n) PRINT *,'y:',NodesM % y(1:n) @@ -7490,7 +7490,7 @@ SUBROUTINE AddNodalProjectorStrongStrides() Nundefined = Nundefined + 1 WRITE( Message,'(A,2I8,3ES12.3)') 'Problematic node: ',& ind,ParEnv % MyPe,x1,y1,MinDist - CALL Warn('LevelProjector',Message) + CALL Warn('AddNodalProjectorStrongStrides',Message) CYCLE END IF @@ -7518,7 +7518,7 @@ SUBROUTINE AddNodalProjectorStrongStrides() END DO IF( Nundefined > 0 ) THEN - CALL Warn('LevelProjector',& + CALL Warn('AddNodalProjectorStrongStrides',& 'Nodes could not be determined by any edge: '//TRIM(I2S(Nundefined))) END IF @@ -7545,7 +7545,7 @@ SUBROUTINE AddNodalProjectorStrongGeneric() TYPE(Nodes_t) :: NodesM LOGICAL :: LeftCircle, Found, Stat - CALL Info('LevelProjector','Creating strong generic projector for nodal dofs',Level=10) + CALL Info('AddNodalProjectorStrongGeneric','Creating strong generic projector for nodal dofs',Level=10) n = Mesh % MaxElementNodes ALLOCATE( NodesM % x(n), NodesM % y(n), NodesM % z(n), Basis(n), coeff(n), coeffi(n) ) @@ -7684,14 +7684,14 @@ SUBROUTINE AddNodalProjectorStrongGeneric() IF(.NOT. Found ) THEN IF( MaxMinBasis > -1.0d-6 ) THEN - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddNodalProjectorStrongGeneric',Message,Level=8) Found = .TRUE. ELSE Nundefined = Nundefined + 1 IF( .NOT. HaveMaxDistance ) THEN WRITE( Message,'(A,2I8,3ES12.3)') 'Problematic node: ',& ind,ParEnv % MyPe,x1,y1,MaxMinBasis - CALL Warn('LevelProjector',Message ) + CALL Warn('AddNodalProjectorStrongGeneric',Message ) END IF END IF END IF @@ -7724,10 +7724,10 @@ SUBROUTINE AddNodalProjectorStrongGeneric() IF( Nundefined > 0 ) THEN IF( HaveMaxDistance ) THEN - CALL Info('LevelProjector',& + CALL Info('AddNodalProjectorStrongGeneric',& 'Nodes could not be found in any element: '//TRIM(I2S(Nundefined))) ELSE - CALL Warn('LevelProjector',& + CALL Warn('AddNodalProjectorStrongGeneric',& 'Nodes could not be found in any element: '//TRIM(I2S(Nundefined))) END IF END IF @@ -7762,7 +7762,7 @@ SUBROUTINE AddEdgeProjectorStrongStrides() SkewEdge, AtRangeLimit - CALL Info('LevelProjector','Creating strong stride projector for edges assuming strides',Level=10) + CALL Info('AddEdgeProjectorStrongStrides','Creating strong stride projector for edges assuming strides',Level=10) n = Mesh % NumberOfEdges IF( n == 0 ) RETURN @@ -8045,7 +8045,7 @@ SUBROUTINE AddEdgeProjectorStrongStrides() ! weight depends on the relative fraction of overlapping IF( ABS( xmax-xmin) < TINY( xmax ) ) THEN - CALL Warn('LevelProjector','Degenerated edge 2?') + CALL Warn('AddEdgeProjectorStrongStrides','Degenerated edge 2?') coeff(ncoeff) = cskew * 1.0_dp ELSE coeff(ncoeff) = cskew * (MIN(xmaxm,xmax)-MAX(xminm,xmin))/(xmax-xmin) @@ -8113,7 +8113,7 @@ SUBROUTINE AddEdgeProjectorStrongStrides() xm2 = coeff(ncoeff) IF( ABS( xm2-xm1) < TINY( xm2 ) ) THEN - CALL Warn('LevelProjector','Degenerated edge 3?') + CALL Warn('AddEdgeProjectorStrongStrides','Degenerated edge 3?') coeff(ncoeff-1) = cskew * 0.5_dp ELSE coeff(ncoeff-1) = cskew * ABS((xm2-xmean)/(xm2-xm1)) @@ -8125,13 +8125,13 @@ SUBROUTINE AddEdgeProjectorStrongStrides() coeff(1) = 1.0_dp ELSE IF( ncoeff >= 2 ) THEN IF( ncoeff > 2 ) THEN - CALL Warn('LevelProjector',& + CALL Warn('AddEdgeProjectorStrongStrides',& 'There should not be more than two target edges: '//TRIM(I2S(ncoeff))) END IF xm1 = coeff(1) xm2 = coeff(2) IF( ABS( xm2-xm1) < TINY( xm2 ) ) THEN - CALL Warn('LevelProjector','Degenerated edge 3?') + CALL Warn('AddEdgeProjectorStrongStrides','Degenerated edge 3?') coeff(1) = 0.5_dp ELSE coeff(1) = ABS((xm2-xmean)/(xm2-xm1)) @@ -8156,10 +8156,10 @@ SUBROUTINE AddEdgeProjectorStrongStrides() Nundefined = Nundefined + 1 WRITE( Message,'(A,2I8,4ES12.3)') 'Problematic edge: ',& eind,ParEnv % MyPe,x1,x2,y1,y2 - CALL Warn('LevelProjector', Message ) + CALL Warn('AddEdgeProjectorStrongStrides', Message ) WRITE( Message,'(A,I8,3L4,4ES12.3)') 'Bounding box: ',& eind,XConst,YConst,Repeating,XminAll,XmaxAll,YminAll,YmaxAll - CALL Warn('LevelProjector', Message ) + CALL Warn('AddEdgeProjectorStrongStrides', Message ) CYCLE END IF @@ -8212,20 +8212,20 @@ SUBROUTINE AddEdgeProjectorStrongStrides() END DO IF( Nundefined > 0 ) THEN - CALL Error('LevelProjector',& + CALL Error('AddEdgeProjectorStrongStrides',& 'Number of edges could not be mapped: '//TRIM(I2S(Nundefined))) END IF WRITE( Message,'(A,ES12.5)') 'Minimum absolute sum of edge weights: ',minwsum - CALL Info('LevelProjector',Message,Level=10) + CALL Info('AddEdgeProjectorStrongStrides',Message,Level=10) WRITE( Message,'(A,ES12.5)') 'Maximum absolute sum of edge weights: ',maxwsum - CALL Info('LevelProjector',Message,Level=10) + CALL Info('AddEdgeProjectorStrongStrides',Message,Level=10) IF( NoSkewed > 0 ) THEN - CALL Info('LevelProjector','Number of skewed edge mappings: '//TRIM(I2S(NoSkewed)),Level=8) + CALL Info('AddEdgeProjectorStrongStrides','Number of skewed edge mappings: '//TRIM(I2S(NoSkewed)),Level=8) END IF - CALL Info('LevelProjector','Created strong constraints for edge dofs',Level=8) + CALL Info('AddEdgeProjectorStrongStrides','Created strong constraints for edge dofs',Level=8) DEALLOCATE( Nodes % x, Nodes % y, Nodes % z, & NodesM % x, NodesM % y, NodesM % z ) @@ -8245,7 +8245,7 @@ SUBROUTINE AddEdgeProjectorStrongConforming() INTEGER, POINTER :: PerPerm(:) LOGICAL, POINTER :: PerFlip(:) - CALL Info('LevelProjector','Creating strong projector for conforming edges',Level=8) + CALL Info('AddEdgeProjectorStrongConforming','Creating strong projector for conforming edges',Level=8) ne = Mesh % NumberOfEdges IF( ne == 0 ) RETURN @@ -8280,7 +8280,7 @@ SUBROUTINE AddEdgeProjectorStrongConforming() DEALLOCATE( PerPerm, PerFlip ) - CALL Info('LevelProjector','Created strong constraints for conforming edge dofs',Level=10) + CALL Info('AddEdgeProjectorStrongConforming','Created strong constraints for conforming edge dofs',Level=10) END SUBROUTINE AddEdgeProjectorStrongConforming @@ -8294,7 +8294,7 @@ SUBROUTINE AddNodeProjectorStrongConforming() INTEGER :: nn, i, nrow, ind, indm, sgn INTEGER, POINTER :: PerPerm(:) - CALL Info('LevelProjector','Creating strong projector for conforming edges',Level=8) + CALL Info('AddNodeProjectorStrongConforming','Creating strong projector for conforming edges',Level=8) nn = Mesh % NumberOfNodes @@ -8326,7 +8326,7 @@ SUBROUTINE AddNodeProjectorStrongConforming() DEALLOCATE( PerPerm ) - CALL Info('LevelProjector','Created strong constraints for conforming node dofs',Level=10) + CALL Info('AddNodeProjectorStrongConforming','Created strong constraints for conforming node dofs',Level=10) END SUBROUTINE AddNodeProjectorStrongConforming @@ -8357,7 +8357,7 @@ SUBROUTINE AddProjectorWeakStrides() LOGICAL :: LeftCircle, Stat TYPE(Mesh_t), POINTER :: Mesh - CALL Info('LevelProjector','Creating weak projector for stride mesh',Level=8) + CALL Info('AddProjectorWeakStrides','Creating weak projector for stride mesh',Level=8) Mesh => CurrentModel % Solver % Mesh @@ -8572,7 +8572,7 @@ SUBROUTINE AddProjectorWeakStrides() kmax = k IF( kmax < 3 ) THEN - CALL Warn('LevelProjector','Cannot integrate over '//TRIM(I2S(kmax))//' nodes') + CALL Warn('AddProjectorWeakStrides','Cannot integrate over '//TRIM(I2S(kmax))//' nodes') CYCLE END IF @@ -8750,11 +8750,11 @@ SUBROUTINE AddProjectorWeakStrides() DEALLOCATE( Basis, BasisM ) DEALLOCATE( dBasisdx, WBasis, WBasisM, RotWBasis ) - CALL Info('LevelProjector','Number of integration pairs: '& + CALL Info('AddProjectorWeakStrides','Number of integration pairs: '& //TRIM(I2S(Ninteg)),Level=10) WRITE( Message,'(A,ES12.3)') 'Maximum error in area integration:',MaxErr - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakStrides',Message,Level=8) END SUBROUTINE AddProjectorWeakStrides @@ -8784,7 +8784,7 @@ SUBROUTINE LocalEdgeSolutionCoeffs( BC, Element, Nodes, ne, nf, PiolaVersion, Se IF( .NOT. Visited ) THEN m = 12 ALLOCATE( Basis(m), WBasis(m,3), RotWBasis(m,3), dBasisdx(m,3), STAT=AllocStat ) - IF( AllocStat /= 0 ) CALL Fatal('AddProjectorWeakGeneric','Allocation error 3') + IF( AllocStat /= 0 ) CALL Fatal('LocalEdgeSolutionCoeffs','Allocation error 3') pCvec => ListGetConstRealArray( BC,'Level Projector Debug Vector',Found) IF( Found ) THEN @@ -8887,7 +8887,7 @@ SUBROUTINE AddProjectorWeakGeneric() REAL(KIND=dp), ALLOCATABLE :: CoeffBasis(:), MASS(:,:) - CALL Info('LevelProjector','Creating weak constraints using a generic integrator',Level=8) + CALL Info('AddProjectorWeakGeneric','Creating weak constraints using a generic integrator',Level=8) Mesh => CurrentModel % Solver % Mesh @@ -8977,7 +8977,7 @@ SUBROUTINE AddProjectorWeakGeneric() DO i=1,BMesh1 % NumberOfNodes IF( BMesh1 % Nodes % x(i)**2 + BMesh1 % Nodes % y(i)**2 < 1.0d-20 ) THEN CenterI = i - CALL Info('LevelProjector','Found center node in slave: '& + CALL Info('AddProjectorWeakGeneric','Found center node in slave: '& //TRIM(I2S(CenterI)),Level=10) EXIT END IF @@ -8985,7 +8985,7 @@ SUBROUTINE AddProjectorWeakGeneric() DO i=1,BMesh2 % NumberOfNodes IF( BMesh2 % Nodes % x(i)**2 + BMesh2 % Nodes % y(i)**2 < 1.0d-20 ) THEN CenterIM = i - CALL Info('LevelProjector','Found center node in master: '& + CALL Info('AddProjectorWeakGeneric','Found center node in master: '& //TRIM(I2S(CenterI)),Level=10) EXIT END IF @@ -10000,58 +10000,58 @@ SUBROUTINE AddProjectorWeakGeneric() DEALLOCATE(CoeffBasis, MASS ) END IF - CALL Info('LevelProjector','Number of integration pair candidates: '& + CALL Info('AddProjectorWeakGeneric','Number of integration pair candidates: '& //TRIM(I2S(TotCands)),Level=10) - CALL Info('LevelProjector','Number of integration pairs: '& + CALL Info('AddProjectorWeakGeneric','Number of integration pairs: '& //TRIM(I2S(TotHits)),Level=10) - CALL Info('LevelProjector','Number of edge intersections: '& + CALL Info('AddProjectorWeakGeneric','Number of edge intersections: '& //TRIM(I2S(EdgeHits)),Level=10) - CALL Info('LevelProjector','Number of corners inside element: '& + CALL Info('AddProjectorWeakGeneric','Number of corners inside element: '& //TRIM(I2S(EdgeHits)),Level=10) - CALL Info('LevelProjector','Number of initial corners: '& + CALL Info('AddProjectorWeakGeneric','Number of initial corners: '& //TRIM(I2S(InitialHits)),Level=10) - CALL Info('LevelProjector','Number of active corners: '& + CALL Info('AddProjectorWeakGeneric','Number of active corners: '& //TRIM(I2S(ActiveHits)),Level=10) - CALL Info('LevelProjector','Number of most subelement corners: '& + CALL Info('AddProjectorWeakGeneric','Number of most subelement corners: '& //TRIM(I2S(MaxSubTriangles)),Level=10) - CALL Info('LevelProjector','Element of most subelement corners: '& + CALL Info('AddProjectorWeakGeneric','Element of most subelement corners: '& //TRIM(I2S(MaxSubElem)),Level=10) WRITE( Message,'(A,ES12.5)') 'Total reference area:',TotRefArea - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,ES12.5)') 'Total integrated area:',TotSumArea - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) Err = TotSumArea / TotRefArea WRITE( Message,'(A,ES15.6)') 'Average ratio in area integration:',Err - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,I0,A,ES12.4)') & 'Maximum relative discrepancy in areas (element: ',MaxErrInd,'):',MaxErr-1.0_dp - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,I0,A,ES12.4)') & 'Minimum relative discrepancy in areas (element: ',MinErrInd,'):',MinErr-1.0_dp - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) - CALL Info('LevelProjector','Number of slave entries: '& + CALL Info('AddProjectorWeakGeneric','Number of slave entries: '& //TRIM(I2S(Nslave)),Level=10) - CALL Info('LevelProjector','Number of master entries: '& + CALL Info('AddProjectorWeakGeneric','Number of master entries: '& //TRIM(I2S(Nmaster)),Level=10) IF( DebugEdge ) THEN CALL ListAddConstReal( CurrentModel % Simulation,'res: err',err) WRITE( Message,'(A,ES15.6)') 'Slave entries total sum:', sums - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,ES15.6)') 'Master entries total sum:', summ - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,ES15.6)') 'Master entries total sum2:', summ2 - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeakGeneric',Message,Level=8) WRITE( Message,'(A,ES15.6)') 'Maximum edge projection error:', MaxEdgeErr - CALL Info('LevelProjector',Message,Level=6) + CALL Info('AddProjectorWeakGeneric',Message,Level=6) CALL ListAddConstReal( CurrentModel % Simulation,'res: sums',sums) CALL ListAddConstReal( CurrentModel % Simulation,'res: summ',summ) @@ -10115,7 +10115,7 @@ SUBROUTINE AddProjectorWeak1D() REAL(KIND=dp), ALLOCATABLE :: CoeffBasis(:), MASS(:,:) - CALL Info('LevelProjector','Creating weak constraints using a 1D integrator',Level=8) + CALL Info('AddProjectorWeak1D','Creating weak constraints using a 1D integrator',Level=8) Mesh => CurrentModel % Solver % Mesh @@ -10487,28 +10487,28 @@ SUBROUTINE AddProjectorWeak1D() DEALLOCATE( NodesT % x, NodesT % y, NodesT % z ) DEALLOCATE( Basis, BasisM ) - CALL Info('LevelProjector','Number of integration pairs: '& + CALL Info('AddProjectorWeak1D','Number of integration pairs: '& //TRIM(I2S(TotHits)),Level=10) IF( AntiPeriodicHits > 0 ) THEN - CALL Info('LevelProjector','Number of antiperiodic pairs: '& + CALL Info('AddProjectorWeak1D','Number of antiperiodic pairs: '& //TRIM(I2S(AntiPeriodicHits)),Level=10) END IF WRITE( Message,'(A,ES12.5)') 'Total reference length:',TotRefArea / ArcCoeff - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeak1D',Message,Level=8) WRITE( Message,'(A,ES12.5)') 'Total integrated length:',TotSumArea / ArcCoeff - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeak1D',Message,Level=8) Err = TotSumArea / TotRefArea WRITE( Message,'(A,ES12.3)') 'Average ratio in length integration:',Err - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeak1D',Message,Level=8) WRITE( Message,'(A,I0,A,ES12.4)') & 'Maximum relative discrepancy in length (element: ',MaxErrInd,'):',MaxErr-1.0_dp - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeak1D',Message,Level=8) WRITE( Message,'(A,I0,A,ES12.4)') & 'Minimum relative discrepancy in length (element: ',MinErrInd,'):',MinErr-1.0_dp - CALL Info('LevelProjector',Message,Level=8) + CALL Info('AddProjectorWeak1D',Message,Level=8) END SUBROUTINE AddProjectorWeak1D @@ -11817,7 +11817,7 @@ SUBROUTINE AxialInterfaceMeshes(BMesh1, BMesh2, BParams ) FullCircle = Hit0 .AND. Hit90 .AND. Hit180 .AND. Hit270 IF( FullCircle ) THEN - CALL Info('RotationalInterfaceMeshes','Axial interface seems to be a full circle',& + CALL Info('AxialInterfaceMeshes','Axial interface seems to be a full circle',& Level=6) EXIT END IF @@ -11840,7 +11840,7 @@ SUBROUTINE AxialInterfaceMeshes(BMesh1, BMesh2, BParams ) ELSE err1 = 2 * ABS( dFii1 - dFii2 ) / ( dFii1 + dFii2 ) WRITE(Message,'(A,ES12.3)') 'Discrepancy in dfii:',err1 - CALL Info('RotationalInterfaceMeshes',Message,Level=8) + CALL Info('AxialInterfaceMeshes',Message,Level=8) Nsymmetry = 360.0_dp / ( MIN( dfii1, dfii2 ) ) END IF @@ -11852,7 +11852,7 @@ SUBROUTINE AxialInterfaceMeshes(BMesh1, BMesh2, BParams ) CALL ListAddInteger(BParams,'Axial Projector Periods', NINT( Nsymmetry ) ) ELSE WRITE(Message,'(A,I0)') 'Using enforced number of periods: ',i - CALL Info('RotationalInterfaceMeshes',Message,Level=8) + CALL Info('AxialInterfaceMeshes',Message,Level=8) END IF END SUBROUTINE AxialInterfaceMeshes @@ -12886,13 +12886,13 @@ SUBROUTINE PeriodicPermutation( Model, Mesh, This, Trgt, PerPerm, PerFlip ) IF( AntiPeriodic ) CALL Info('PeriodicPermutation','Assuming antiperiodic conforming projector',Level=8) - IF( Radial ) CALL Info('PeriodicProjector','Enforcing > Radial Projector <',Level=12) - IF( Axial ) CALL Info('PeriodicProjector','Enforcing > Axial Projector <',Level=12) - IF( Sliding ) CALL Info('PeriodicProjector','Enforcing > Sliding Projector <',Level=12) - IF( Cylindrical ) CALL Info('PeriodicProjector','Enforcing > Cylindrical Projector <',Level=12) - IF( Rotational ) CALL Info('PeriodicProjector','Enforcing > Rotational Projector <',Level=12) - IF( Flat ) CALL Info('PeriodicProjector','Enforcing > Flat Projector <',Level=12) - IF( Plane ) CALL Info('PeriodicProjector','Enforcing > Plane Projector <',Level=12) + IF( Radial ) CALL Info('PeriodicPermutation','Enforcing > Radial Projector <',Level=12) + IF( Axial ) CALL Info('PeriodicPermutation','Enforcing > Axial Projector <',Level=12) + IF( Sliding ) CALL Info('PeriodicPermutation','Enforcing > Sliding Projector <',Level=12) + IF( Cylindrical ) CALL Info('PeriodicPermutation','Enforcing > Cylindrical Projector <',Level=12) + IF( Rotational ) CALL Info('PeriodicPermutation','Enforcing > Rotational Projector <',Level=12) + IF( Flat ) CALL Info('PeriodicPermutation','Enforcing > Flat Projector <',Level=12) + IF( Plane ) CALL Info('PeriodicPermutation','Enforcing > Plane Projector <',Level=12) DoNodes = .TRUE. IF( ListGetLogical( Model % Solver % Values,'Projector Skip Nodes',GotIt ) ) DoNodes = .FALSE. @@ -12907,7 +12907,7 @@ SUBROUTINE PeriodicPermutation( Model, Mesh, This, Trgt, PerPerm, PerFlip ) IF( DoEdges ) THEN IF(isPelement(Mesh % Elements(1))) THEN DoEdges = .FALSE. - CALL Info('PeriodicProjector','Edge projector will not be created for p-element mesh',Level=10) + CALL Info('PeriodicPermutation','Edge projector will not be created for p-element mesh',Level=10) END IF END IF @@ -14390,7 +14390,7 @@ SUBROUTINE FindMeshEdges2D( Mesh, BulkMask ) END DO CALL Info('FindMeshEdges2D','Creating hash table of size '& - //TRIM(I2S(Mesh % NumberOfNodes))//' for noto-to-node connectivity',Level=12) + //TRIM(I2S(Mesh % NumberOfNodes))//' for node-to-node connectivity',Level=12) ALLOCATE( HashTable( Mesh % NumberOfNodes ) ) DO i=1,Mesh % NumberOfNodes NULLIFY( HashTable(i) % Head ) @@ -20003,7 +20003,7 @@ SUBROUTINE ClusterElementsByDirection(Params,Mesh,Clustering,MaskActive) Divisions(3) = ( nsize / ( clustersize * Divisions(1) * Divisions(2) ) ) END IF ELSE - CALL Fatal('ClusterNodesByDirection','Clustering Divisions not given!') + CALL Fatal('ClusterElementsByDirection','Clustering Divisions not given!') END IF END IF @@ -22228,7 +22228,7 @@ FUNCTION MinimalElementalSet( Mesh, JumpMode, VarPerm, BcFlag, & LOGICAL :: Found - CALL Info('MinimalDiscontSet','Creating discontinuous subset from DG field',Level=5) + CALL Info('MinimalElementalSet','Creating discontinuous subset from DG field',Level=5) ! Calculate size of permutation vector ALLOCATE( NodeVisited( Mesh % NumberOfNodes ) ) @@ -22403,10 +22403,10 @@ SUBROUTINE ReduceElementalVar( Mesh, Var, SetPerm, TakeAverage ) END IF IF( TakeAverage ) THEN - CALL Info('CalculateSetAverage','Calculating reduced set average for: '& + CALL Info('ReduceElementalVar','Calculating reduced set average for: '& //TRIM(Var % Name), Level=7) ELSE - CALL Info('CalculateSetAverage','Calculating reduced set sum for: '& + CALL Info('ReduceElementalVar','Calculating reduced set sum for: '& //TRIM(Var % Name), Level=7) END IF diff --git a/fem/src/ModelDescription.F90 b/fem/src/ModelDescription.F90 index 9c48444de2..8bda3457a9 100644 --- a/fem/src/ModelDescription.F90 +++ b/fem/src/ModelDescription.F90 @@ -472,7 +472,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & SimulationId = Name(15:) ELSE WRITE( Message, * ) 'Unknown input field in header section: ' // TRIM(Name) - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF END DO @@ -605,7 +605,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfBCs ) THEN WRITE( Message, * ) 'Boundary Condition section number ('//TRIM(I2S(Arrayn))// & ') exceeds number of BCs ('//TRIM(I2S(Model % NumberOfBCs))//')' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF Model % BCs(ArrayN) % Tag = ArrayN List => Model % BCs(Arrayn) % Values @@ -637,7 +637,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & Model % NumberOfBoundaries ) THEN WRITE( Message, * ) 'Boundary section number: ',BoundaryIndex, & ' exceeds header value.' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF Model % BoundaryId(BoundaryIndex) = Arrayn List => Model % Boundaries(BoundaryIndex) % Values @@ -686,7 +686,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfICs ) THEN WRITE( Message, * ) 'Initial Condition section number: ',Arrayn, & ' exceeds header value.' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF Model % ICs(ArrayN) % Tag = ArrayN List => Model % ICs(Arrayn) % Values @@ -738,7 +738,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfMaterials ) THEN WRITE( Message, * ) 'Material section number: ',Arrayn, & ' exceeds header value.' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % Materials(Arrayn) % Values END IF @@ -785,7 +785,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfBodyForces ) THEN WRITE( Message, * ) 'Body Force section number: ',Arrayn, & ' exceeds header value.' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % BodyForces(Arrayn) % Values END IF @@ -833,7 +833,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfEquations ) THEN WRITE( Message, * ) 'Equation section number: ',Arrayn, & ' exceeds header value.' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % Equations(ArrayN) % Values END IF @@ -883,7 +883,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfBodies ) THEN WRITE( Message, * ) 'Body section number: ',Arrayn, & ' exceeds header value. Aborting. ' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % Bodies(Arrayn) % Values END IF @@ -932,7 +932,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfComponents ) THEN WRITE( Message, * ) 'Component section number: ',Arrayn, & ' exceeds header value. Aborting. ' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % Components(Arrayn) % Values END IF @@ -996,13 +996,13 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & IF ( Arrayn <= 0 .OR. Arrayn > Model % NumberOfSolvers ) THEN WRITE( Message, * ) 'Solver section number: ',Arrayn, & ' exceeds header value. Aborting. ' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF List => Model % Solvers(Arrayn) % Values END IF ELSE WRITE( Message, * ) 'Unknown input section name: ',TRIM(Section) - CALL Fatal( 'Model Input', Message ) + CALL Fatal( Caller, Message ) END IF !------------------------------------------------------------------------------ @@ -1316,7 +1316,7 @@ RECURSIVE SUBROUTINE LoadInputFile( Model, InFileUnit, FileName, & 10 CONTINUE WRITE( Message, * ) 'Cannot find input file: ', TRIM(FileName) - CALL Warn( 'Model Input', Message ) + CALL Warn( Caller, Message ) CONTAINS @@ -1376,7 +1376,7 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) INQUIRE(FILE=TRIM(str1), EXIST=fexist) END IF IF (.NOT. fexist) THEN - CALL Fatal('CheckKeyWord', 'SOLVER.KEYWORDS not found') + CALL Fatal('CheckKeyword', 'SOLVER.KEYWORDS not found') END IF OPEN( 1, FILE=TRIM(str1), STATUS='OLD', ERR=10 ) @@ -1387,12 +1387,12 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) hash => HashCreate( 50,4 ) IF ( .NOT. ASSOCIATED( hash ) ) THEN IF ( CheckAbort <= 2 ) THEN - CALL Warn( 'Model Input', 'Can not create the hash table for SOLVER.KEYWORDS.' ) - CALL Warn( 'Model Input', 'keyword checking disabled.' ) + CALL Warn( 'CheckKeyword', 'Can not create the hash table for SOLVER.KEYWORDS.' ) + CALL Warn( 'CheckKeyword', 'keyword checking disabled.' ) CheckAbort = 0 RETURN ELSE - CALL Fatal( 'Model Input','Can not create the hash table for SOLVER.KEYWORDS.' ) + CALL Fatal( 'CheckKeyword','Can not create the hash table for SOLVER.KEYWORDS.' ) END IF END IF @@ -1412,12 +1412,12 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) IF ( istat /= 0 ) THEN IF ( CheckAbort <= 2 ) THEN - CALL Warn( 'Model Input', 'Can not allocate the hash table entry for SOLVER.KEYWORDS.' ) - CALL Warn( 'Model Input', ' keyword checking disabled.' ) + CALL Warn( 'CheckKeyword', 'Can not allocate the hash table entry for SOLVER.KEYWORDS.' ) + CALL Warn( 'CheckKeyword', ' keyword checking disabled.' ) CheckAbort = 0 RETURN ELSE - CALL Fatal( 'Model Input', 'Can not allocate the hash table entry for SOLVER.KEYWORDS.' ) + CALL Fatal( 'CheckKeyword', 'Can not allocate the hash table entry for SOLVER.KEYWORDS.' ) END IF END IF @@ -1426,11 +1426,11 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) lstat = HashAdd( hash, str1, Val ) IF ( .NOT. lstat ) THEN IF ( CheckAbort <= 2 ) THEN - CALL Warn( 'Model Input', 'Hash table build error. Keyword checking disabled.' ) + CALL Warn( 'CheckKeyword', 'Hash table build error. Keyword checking disabled.' ) CheckAbort = 0 RETURN ELSE - CALL Fatal( 'Model Input', 'Hash table build error.' ) + CALL Fatal( 'CheckKeyword', 'Hash table build error.' ) END IF END IF END DO @@ -1439,7 +1439,7 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) IF ( FirstTime ) THEN FirstTime = .FALSE. OPEN( 1, FILE='SOLVER.KEYWORDS', STATUS='OLD', ERR=6 ) - CALL Info( 'Model Input', 'Found local SOLVER.KEYWORDS file, ' // & + CALL Info( 'CheckKeyword', 'Found local SOLVER.KEYWORDS file, ' // & 'adding keywords to runtime database.' ) GOTO 5 6 CONTINUE @@ -1495,7 +1495,7 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) IF ( .NOT. ( ScanOnly .OR. CheckAbort == 2) ) THEN WRITE( Message, * ) 'Unlisted keyword: [', TRIM(name), & '] in section: [', TRIM(Section), ']' - CALL Info( 'Model Input', Message ) + CALL Info( 'CheckKeyword', Message ) ! This is intended to be activated when new keywords are checked ! Generally it can be set false @@ -1524,12 +1524,12 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) '] in section: [', TRIM(Section), ']', & ' is given wrong type: [', TRIM(TYPE), & '], should be of type: [', TRIM(Val % TYPE),']' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( 'CheckKeyword', Message ) END IF ELSE WRITE( Message, * ) 'Unlisted keyword: [', TRIM(name), & '] in section: [', TRIM(Section), '].' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( 'CheckKeyword', Message ) END IF RETURN @@ -1537,10 +1537,10 @@ SUBROUTINE CheckKeyWord( Name,TYPE,CheckAbort,FreeNames,Section,ReturnType ) 10 CONTINUE IF ( CheckAbort <= 2 ) THEN - CALL Warn( 'Model Input', 'Keyword check requested, but SOLVER.KEYWORDS' // & + CALL Warn( 'CheckKeyword', 'Keyword check requested, but SOLVER.KEYWORDS' // & ' database not available.' ) ELSE - CALL Fatal( 'Model Input', 'Keyword check requested, but SOLVER.KEYWORDS' // & + CALL Fatal( 'CheckKeyword', 'Keyword check requested, but SOLVER.KEYWORDS' // & ' database not available.' ) END IF !------------------------------------------------------------------------------ @@ -1587,7 +1587,7 @@ RECURSIVE SUBROUTINE SectionContents( Model,List, CheckAbort,FreeNames, & IF ( SEQL(Name,'include') ) THEN OPEN( InFileUnit-1,FILE=TRIM(Name(9:)),STATUS='OLD',IOSTAT=iostat) IF( iostat /= 0 ) THEN - CALL Fatal( 'Model Input','Cannot find include file: '//TRIM(Name(9:))) + CALL Fatal( 'SectionContents','Cannot find include file: '//TRIM(Name(9:))) END IF CALL SectionContents( Model,List,CheckAbort,FreeNames, & @@ -1691,7 +1691,7 @@ RECURSIVE SUBROUTINE SectionContents( Model,List, CheckAbort,FreeNames, & IF ( .NOT. ScanOnly ) THEN SELECT CASE ( TYPE ) CASE (LIST_TYPE_CONSTANT_SCALAR ) - call Fatal('ModelDescription', 'Constant expressions are not supported with Lua. & + call Fatal('SectionContents', 'Constant expressions are not supported with Lua. & Please provide at least a dummy argument.') IF ( SizeGiven ) THEN @@ -2066,13 +2066,13 @@ SUBROUTINE SyntaxError( Section, Name, LastString ) !------------------------------------------------------------------------------ CHARACTER(LEN=*) :: Section, Name, LastString - CALL Error( 'Model Input', ' ' ) + CALL Error( 'LoadInputFile', ' ' ) WRITE( Message, * ) 'Unknown specifier: [',TRIM(LastString),']' - CALL Error( 'Model Input', Message ) + CALL Error( 'LoadInputFile', Message ) WRITE( Message, * ) 'In section: [', TRIM(Section), ']' - CALL Error( 'Model Input', Message ) + CALL Error( 'LoadInputFile', Message ) WRITE( Message, * ) 'For property name:[',TRIM(Name),']' - CALL Fatal( 'Model Input', Message ) + CALL Fatal( 'LoadInputFile', Message ) !------------------------------------------------------------------------------ END SUBROUTINE SyntaxError !------------------------------------------------------------------------------ @@ -2324,7 +2324,7 @@ FUNCTION LoadModel( ModelName,BoundariesOnly,numprocs,mype,MeshIndex) RESULT( Mo !$OMP CRITICAL LuaState = lua_init() IF(.NOT. LuaState % Initialized) THEN - CALL Fatal('ModelDescription', 'Failed to initialize Lua subsystem.') + CALL Fatal('LoadModel', 'Failed to initialize Lua subsystem.') END IF ! Store mpi task and omp thread ids in a table @@ -2492,7 +2492,7 @@ FUNCTION LoadModel( ModelName,BoundariesOnly,numprocs,mype,MeshIndex) RESULT( Mo Single = ListGetLogical( Model % Simulation,'Partition Mesh', GotIt ) IF ( Single ) THEN IF( ParEnv % PEs == 1 ) THEN - CALL Warn('LoadMesh','Why perform partitioning in serial case?') + CALL Warn('LoadModel','Why perform partitioning in serial case?') END IF IF( ParEnv % MyPe == 0 ) THEN SerialMesh => LoadMesh2( Model,MeshDir,MeshName,BoundariesOnly,& @@ -2505,7 +2505,7 @@ FUNCTION LoadModel( ModelName,BoundariesOnly,numprocs,mype,MeshIndex) RESULT( Mo IF( ParEnv % PEs > 1) THEN Model % Meshes => ReDistributeMesh( Model, SerialMesh, .FALSE., .TRUE. ) ELSE - CALL Info('LoadMesh','Only one active partition, using the serial mesh as it is!') + CALL Info('LoadModel','Only one active partition, using the serial mesh as it is!') IF( MAXVAL( SerialMesh % RePartition ) <= 1 ) THEN DEALLOCATE( SerialMesh % RePartition ) END IF @@ -5699,7 +5699,7 @@ END SUBROUTINE FreeModel !> This routine makes it possible to refer to the parameters !> in the .sif file by rpar(0), rpar(1),... !----------------------------------------------------------------------------- - SUBROUTINE SetParametersMATC(NoParam,Param) + SUBROUTINE SetRealParametersMATC(NoParam,Param) INTEGER :: NoParam REAL(KIND=dp), ALLOCATABLE :: Param(:) @@ -5719,8 +5719,34 @@ SUBROUTINE SetParametersMATC(NoParam,Param) !$OMP END PARALLEL END DO - END SUBROUTINE SetParametersMATC + END SUBROUTINE SetRealParametersMATC + !------------------------------------------------------------------------------ + !> This routine makes it possible to refer to the parameters + !> in the .sif file by rpar(0), rpar(1),... + !----------------------------------------------------------------------------- + SUBROUTINE SetIntegerParametersMATC(NoParam,Param) + + INTEGER :: NoParam + INTEGER, ALLOCATABLE :: Param(:) + + INTEGER :: i,j,tj + CHARACTER(LEN=MAX_STRING_LEN) :: cmd, tmp_str, tcmd, ttmp_str + + DO i=1,NoParam + WRITE( cmd, * ) 'ipar('//TRIM(i2s(i-1))//')=', Param(i) + j = LEN_TRIM(cmd) + !$OMP PARALLEL DEFAULT(NONE) & + !$OMP SHARED(cmd, tmp_str, j ) & + !$OMP PRIVATE(tcmd, ttmp_str, tj) + tj = j + tcmd = cmd + CALL matc( tcmd, ttmp_str, tj ) + !$OMP END PARALLEL + END DO + + END SUBROUTINE SetIntegerParametersMATC + !------------------------------------------------------------------------------ !> Adds parameters used in the simulation either predefined or from run control. @@ -5804,7 +5830,7 @@ SUBROUTINE ControlParameters(Params,piter,GotParams,FinishEarly,PostSimulation) END IF ! Set parametes to be accessible to the MATC preprocessor when reading sif file. - CALL SetParametersMATC(NoParam,Param) + CALL SetRealParametersMATC(NoParam,Param) CALL Info(Caller, '-----------------------------------------', Level=5 ) diff --git a/fem/src/ParallelUtils.F90 b/fem/src/ParallelUtils.F90 index 5ffb5460a8..577bacd9c3 100644 --- a/fem/src/ParallelUtils.F90 +++ b/fem/src/ParallelUtils.F90 @@ -272,8 +272,10 @@ SUBROUTINE ParallelInitMatrix( Solver, Matrix, inPerm ) DO i=1,Mesh % NumberOfBulkElements Element=>Mesh % Elements(i) - bdofs = Solver % Def_Dofs(Element % Type % ElementCode/100, & - Element % Bodyid, 5) + j = Element % Type % ElementCode/100 + bdofs = Solver % Def_Dofs(j, Element % Bodyid, 5) + IF ( bdofs<=0 .AND. & + Solver % Def_Dofs(j,Element % Bodyid,6)>1) bdofs = Element % BDOFs DO l=1,bdofs DO j=1,DOFs @@ -631,6 +633,7 @@ SUBROUTINE ParallelInitMatrix( Solver, Matrix, inPerm ) END DO END IF END DO + DO i=1,n MtrxN => MatrixPI % NeighbourList(i) IF ( .NOT.ASSOCIATED( MtrxN % Neighbours) ) THEN diff --git a/fem/src/Radiation.F90 b/fem/src/Radiation.F90 index 749045aff3..8317db3a81 100644 --- a/fem/src/Radiation.F90 +++ b/fem/src/Radiation.F90 @@ -65,13 +65,19 @@ FUNCTION ComputeRadiationLoad( Model, Mesh, Element, Temperature, & REAL(KIND=dp) :: Asum TYPE(Element_t),POINTER :: RadElement - INTEGER :: i,j,n, bindex + INTEGER :: i,j,n, bindex,nf REAL(KIND=dp), POINTER :: Vals(:) INTEGER, POINTER :: Cols(:) REAL(KIND=dp) :: A1,A2,Emissivity1 LOGICAL :: Found !------------------------------------------------------------------------------ + IF( .NOT. ASSOCIATED( Element % BoundaryInfo % GebhardtFactors ) ) THEN + CALL Fatal('ComputeRadiationLoad','Gebhardt factors not calculated for boundary!') + END IF + + nf = Element % BoundaryInfo % GebhardtFactors % NumberOfFactors + IF(PRESENT(Areas) .AND. PRESENT(Emiss) ) THEN bindex = Element % ElementIndex - Mesh % NumberOfBulkElements @@ -82,7 +88,7 @@ FUNCTION ComputeRadiationLoad( Model, Mesh, Element, Temperature, & T = 0._dp Asum = 0._dp - DO i=1,Element % BoundaryInfo % GebhardtFactors % NumberOfFactors + DO i=1,nf RadElement => Mesh % Elements(Cols(i)) n = RadElement % TYPE % NumberOfNodes @@ -100,7 +106,7 @@ FUNCTION ComputeRadiationLoad( Model, Mesh, Element, Temperature, & T = 0.0_dp Asum = 0.0_dp - DO i=1,Element % BoundaryInfo % GebhardtFactors % NumberOfFactors + DO i=1,nf RadElement => Mesh % Elements(Cols(i)) n = RadElement % TYPE % NumberOfNodes diff --git a/fem/src/SOLVER.KEYWORDS b/fem/src/SOLVER.KEYWORDS index b97f931b3f..e83a112eea 100644 --- a/fem/src/SOLVER.KEYWORDS +++ b/fem/src/SOLVER.KEYWORDS @@ -369,10 +369,13 @@ BodyForce:Real: 'Body Force 2' BodyForce:Real: 'Body Force 3' BodyForce:Real: 'Charge Density' BodyForce:Real: 'Concentration Diffusion Source' +BodyForce:Real: 'Current Density' BodyForce:Real: 'Current Density 1' BodyForce:Real: 'Current Density 2' BodyForce:Real: 'Current Density 3' -BodyForce:Real: 'Current Density' +BodyForce:Real: 'Current Density Im 1' +BodyForce:Real: 'Current Density Im 2' +BodyForce:Real: 'Current Density Im 3' BodyForce:Real: 'Electric Potential' BodyForce:Real: 'Current Phase Angle' BodyForce:Real: 'Current Source' @@ -403,6 +406,9 @@ BodyForce:Real: 'Magnetic BodyForce 3' BodyForce:Real: 'Magnetization 1' BodyForce:Real: 'Magnetization 2' BodyForce:Real: 'Magnetization 3' +BodyForce:Real: 'Magnetization Im 1' +BodyForce:Real: 'Magnetization Im 2' +BodyForce:Real: 'Magnetization Im 3' BodyForce:Real: 'Mesh Origin' BodyForce:Real: 'Mesh Relax' BodyForce:Real: 'Mesh Rotate 1' @@ -431,6 +437,7 @@ BodyForce:Real: 'Smart Heater Control Point' BodyForce:Real: 'Smart Heater Temperature' BodyForce:Real: 'Sound Source' BodyForce:Real: 'Source' +BodyForce:Real: 'Source Field' BodyForce:Real: 'Strain' BodyForce:Real: 'Stress Bodyforce 1' BodyForce:Real: 'Stress Bodyforce 2' @@ -579,7 +586,9 @@ Material:Real: 'Convection Velocity 1' Material:Real: 'Convection Velocity 2' Material:Real: 'Convection Velocity 3' Material:Real: 'Critical Shear Rate' +Material:Real: 'Cross Section Area' Material:Real: 'Damping' +Material:Real: 'Director' Material:Real: 'Rayleigh Damping Alpha' Material:Real: 'Rayleigh Damping Beta' Material:Real: 'Density' @@ -636,6 +645,7 @@ Material:Real: 'Material Constants' Material:Real: 'Material Coordinates Unit Vector 1' Material:Real: 'Material Coordinates Unit Vector 2' Material:Real: 'Material Coordinates Unit Vector 3' +Material:Real: 'Material Parameter' Material:Real: 'Max FreeSurface' Material:Real: 'Mean free path' Material:Real: 'Melting Point' @@ -657,6 +667,7 @@ Material:Real: 'Porous Resistivity' Material:Real: 'Positive Ion Density' Material:Real: 'Potential Difference' Material:Real: 'Pressure Coefficient' +Material:Real: 'Principal direction 2' Material:Real: 'Re Body Force 1' Material:Real: 'Re Body Force 2' Material:Real: 'Re Body Force 3' @@ -671,6 +682,8 @@ Material:Real: 'Reluctivity' Material:Real: 'Residual Water Content' Material:Real: 'Saturated Hydraulic Conductivity' Material:Real: 'Saturated Water Content' +Material:Real: 'Second Moment of Area 2' +Material:Real: 'Second Moment of Area 3' Material:Real: 'Smagorinsky Constant' Material:Real: 'Sound Damping' Material:Real: 'Sound Reaction Damping' @@ -689,6 +702,7 @@ Material:Real: 'Tangent Velocity 2' Material:Real: 'Tangent Velocity 3' Material:Real: 'Tension' Material:Real: 'Thickness' +Material:Real: 'Torsional Constant' Material:Real: 'Transmissivity' Material:Real: 'Viscosity Difference' Material:Real: 'Viscosity Exponent' @@ -836,6 +850,7 @@ Solver:Integer: 'Exec Intervals' Solver:Integer: 'Extend Elastic Layers' Solver:Integer: 'Extract Interval' Solver:Integer: 'Fileindex Offset' +Solver:Integer: 'Fluid Solver Index' Solver:Integer: 'Free Surface After Iterations' Solver:Integer: 'Gebhardt Factors Fixed After Iterations' Solver:Integer: 'IDRS Parameter' @@ -875,6 +890,8 @@ Solver:Integer: 'Max Outer Iterations' Solver:Integer: 'Mesh Rotation Axis Order' Solver:Integer: 'Multigrid Levels' Solver:Integer: 'NOFnuclei' +Solver:Integer: 'Nonlinear Pre Solvers' +Solver:Integer: 'Nonlinear Post Solvers' Solver:Integer: 'Nonlinear System Linesearch Iterations' Solver:Integer: 'Nonlinear System Max Iterations' Solver:Integer: 'Nonlinear System Max Stepsize Tests' @@ -888,8 +905,11 @@ Solver:Integer: 'Number Of Particles' Solver:Integer: 'Number of Eigenmodes Included' Solver:Integer: 'Parasails MaxLevel' Solver:Integer: 'Parasails Symmetry' +Solver:Integer: 'Plate Solver Index' Solver:Integer: 'Population Size' +Solver:Integer: 'Post Solvers' Solver:Integer: 'Potential Solver ID' +Solver:Integer: 'Pre Solvers' Solver:Integer: 'Reinitialize Interval' Solver:Integer: 'Relative Integration Order' Solver:Integer: 'Reload Range Maximum' @@ -899,14 +919,13 @@ Solver:Integer: 'Reload Starting Position' Solver:Integer: 'Runge-Kutta Order' Solver:Integer: 'Save Points' Solver:Integer: 'Scan Points' +Solver:Integer: 'Shell Solver Index' Solver:Integer: 'Show Norm Index' Solver:Integer: 'Slave Solvers' -Solver:Integer: 'Post Solvers' -Solver:Integer: 'Pre Solvers' -Solver:Integer: 'Nonlinear Post Solvers' -Solver:Integer: 'Nonlinear Pre Solvers' +Solver:Integer: 'Solid Solver Index' Solver:Integer: 'Start GRPulay after iterations' Solver:Integer: 'Stream Function First Node' +Solver:Integer: 'Structure Solver Index' Solver:Integer: 'Time Derivative Order' Solver:Integer: 'Time Order' Solver:Integer: 'Variable DOFs' @@ -939,6 +958,7 @@ Solver:Logical: 'Block A-V System' Solver:Logical: 'Block Diagonal A' Solver:Logical: 'Block Gauss-Seidel' Solver:Logical: 'Block Matrix ReUse' +Solver:Logical: 'Block Monolithic' Solver:Logical: 'Block Preconditioner' Solver:Logical: 'Block Preconditioning' Solver:Logical: 'Block Solver' @@ -1001,6 +1021,7 @@ Solver:Logical: 'Discontinuous Galerkin' Solver:Logical: 'Displace Mesh' Solver:Logical: 'Displaced Shape' Solver:Logical: 'Div Discretization' +Solver:Logical: 'Drilling DOFs' Solver:Logical: 'Dx Format' Solver:Logical: 'Edge Basis' Solver:Logical: 'Eigen Analysis' @@ -1029,6 +1050,7 @@ Solver:Logical: 'Harmonic Analysis' Solver:Logical: 'Harmonic Simulation' Solver:Logical: 'Hole Correction' Solver:Logical: 'Impose Body Force Current' +Solver:Logical: 'Incompressible' Solver:Logical: 'Initialize' Solver:Logical: 'Initialize State Variables' Solver:Logical: 'Internal Move Boundary' @@ -1052,6 +1074,7 @@ Solver:Logical: 'Linear System Componentwise Backward Error' Solver:Logical: 'Linear System Normwise Backward Error' Solver:Logical: 'Lumped Mass Matrix' Solver:Logical: 'Matrix Topology Fixed' +Solver:Logical: 'Maxwell Material' Solver:Logical: 'MG Algebraic' Solver:Logical: 'MG Coarse Nodes Save' Solver:Logical: 'MG Compatible Relax Merit Only' @@ -1093,6 +1116,7 @@ Solver:Logical: 'Particle Locate Robust' Solver:Logical: 'Perform Mapping' Solver:Logical: 'Perturbations' Solver:Logical: 'Plane Stress' +Solver:Logical: 'Plate Solver' Solver:Logical: 'Quadratic Approximation' Solver:Logical: 'Radiation Solver' Solver:Logical: 'Rarefaction' @@ -1108,6 +1132,7 @@ Solver:Logical: 'Save Bulk Only' Solver:Logical: 'Save Elemental Fields' Solver:Logical: 'Save Nodal Fields' Solver:Logical: 'Save Halo Elements Only' +Solver:Logical: 'Shell Solver' Solver:Logical: 'Skip Halo Elements' Solver:Logical: 'Save Bulk System' Solver:Logical: 'Save Displacements' @@ -1122,6 +1147,7 @@ Solver:Logical: 'Save Heat Flux' Solver:Logical: 'Scan Frequency' Solver:Logical: 'Scan Position' Solver:Logical: 'Second Kind Basis' +Solver:Logical: 'Separate Magnetic Energy' Solver:Logical: 'Shear Stress Output' Solver:Logical: 'Show Norm' Solver:Logical: 'Side Correction' @@ -1138,6 +1164,7 @@ Solver:Logical: 'Stokes Stream Function' Solver:Logical: 'Stream Function Scaling' Solver:Logical: 'Stream Function Shifting' Solver:Logical: 'Stress Computation' +Solver:Logical: 'Structure-Structure Coupling' Solver:Logical: 'Sum Forces' Solver:Logical: 'Symmetrisize' Solver:Logical: 'Target Variable Complex' @@ -1149,6 +1176,7 @@ Solver:Logical: 'Update Transient System' Solver:Logical: 'Update View Factors' Solver:Logical: 'Use Absolute Norm For Convergence' Solver:Logical: 'Use Accumulation' +Solver:Logical: 'Use Density' Solver:Logical: 'Use Global Mass Matrix' Solver:Logical: 'Use Truncation' Solver:Logical: 'Use Velocity Laplacian' @@ -1424,8 +1452,6 @@ solver:string: 'divergence variable' solver:string: 'velocity initialization method' solver:string: 'velocity variable name' solver:string: 'weight variable' -material:real: 'inverse relative permeability' -material:real: 'inverse relative permeability im' solver:logical: 'calculate energy norm' solver:string: 'field variable' solver:string: 'eletric field variable' @@ -1447,7 +1473,6 @@ solver:logical: 'calculate energy functional' material:real: 'relative reluctivity' material:real: 'relative reluctivity im' material:real: 'reluctivity im' -material:real: 'inverse relative permeability im' bc:real: 'e re {f}' bc:real: 'e re {e}' bc:real: 'e im {f}' diff --git a/fem/src/SolidMechanicsUtils.F90 b/fem/src/SolidMechanicsUtils.F90 new file mode 100644 index 0000000000..a47f69d1aa --- /dev/null +++ b/fem/src/SolidMechanicsUtils.F90 @@ -0,0 +1,648 @@ +!/*****************************************************************************/ +! * +! * Elmer, A Finite Element Software for Multiphysical Problems +! * +! * Copyright 1st April 1995 - , CSC - IT Center for Science Ltd., Finland +! * +! * This library is free software; you can redistribute it and/or +! * modify it under the terms of the GNU Lesser General Public +! * License as published by the Free Software Foundation; either +! * version 2.1 of the License, or (at your option) any later version. +! * +! * This library is distributed in the hope that it will be useful, +! * but WITHOUT ANY WARRANTY; without even the implied warranty of +! * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +! * Lesser General Public License for more details. +! * +! * You should have received a copy of the GNU Lesser General Public +! * License along with this library (in file ../LGPL-2.1); if not, write +! * to the Free Software Foundation, Inc., 51 Franklin Street, +! * Fifth Floor, Boston, MA 02110-1301 USA +! * +! *****************************************************************************/ +! +!/****************************************************************************** +! * +! * Some common subroutines for the solvers of solid mechanics +! * +! * Authors: Mika Malinen, Mikko Lyly +! * Email: mika.malinen@csc.fi +! * Web: http://www.csc.fi/elmer +! * Address: CSC - IT Center for Science Ltd. +! * Keilaranta 14 +! * 02101 Espoo, Finland +! * +! * Original Date: Oct 8, 2020 +! * +! *****************************************************************************/ + +MODULE SolidMechanicsUtils + + USE DefUtils + IMPLICIT NONE + +CONTAINS + +!------------------------------------------------------------------------------ +!> Integrate and assemble the local stiffness matrix corresponding to the +!> one-dimensional Timoshenko beam equations. The local DOFs always +!> correspond to the displacement components along the tangent direction and the +!> principal axes of the cross section. The transformation to global DOFs is done +!> within this subroutine. The stiffness matrix K corresponding to the global +!> DOFs is thus obtained as K = R^T k R and the RHS vector F is obtained as +!> F = R^T f. +!------------------------------------------------------------------------------ + SUBROUTINE BeamStiffnessMatrix(Element, n, nd, nb, TransientSimulation, & + MassAssembly, HarmonicAssembly, LargeDeflection, LocalSol, RHSForce, & + CombineWithShell) +!------------------------------------------------------------------------------ + IMPLICIT NONE + TYPE(Element_t), POINTER, INTENT(IN) :: Element + INTEGER, INTENT(IN) :: n, nd, nb + LOGICAL, INTENT(IN) :: TransientSimulation + LOGICAL, INTENT(IN) :: MassAssembly ! To activate mass matrix integration + LOGICAL, OPTIONAL, INTENT(IN) :: HarmonicAssembly ! To activate the global mass matrix updates + LOGICAL, OPTIONAL, INTENT(IN) :: LargeDeflection ! To activate nonlinear terms + REAL(KIND=dp), OPTIONAL, INTENT(IN) :: LocalSol(:,:) ! The previous solution iterate + REAL(KIND=dp), OPTIONAL, INTENT(OUT) :: RHSForce(:) ! Local RHS vector corresponding to external loads + LOGICAL, OPTIONAL, INTENT(IN) :: CombineWithShell ! Set .TRUE. if the caller is the shell solver +!------------------------------------------------------------------------------ + TYPE(ValueList_t), POINTER :: BodyForce, Material + TYPE(Nodes_t) :: Nodes, LocalNodes + TYPE(GaussIntegrationPoints_t) :: IP + + LOGICAL :: Found, Stat + LOGICAL :: NonlinAssembly + + INTEGER :: DOFs + INTEGER :: i, t, p, q + INTEGER :: i0, p0, q0 + + REAL(KIND=dp), POINTER :: ArrayPtr(:,:) => NULL() + REAL(KIND=dp), POINTER :: StiffBlock(:,:), MassBlock(:,:) + REAL(KIND=dp), DIMENSION(3), PARAMETER :: ZBasis = (/ 0.0d0, 0.0d0, 0.1d1 /) + + REAL(KIND=dp), TARGET :: Mass(6*nd,6*nd), Stiff(6*nd,6*nd), Damp(6*nd,6*nd) + REAL(KIND=dp) :: Force(6*nd) + REAL(KIND=dp) :: RBlock(3,3), R(6*nd,6*nd) + REAL(KIND=dp) :: Basis(nd), dBasis(nd,3), DetJ, Weight + REAL(KIND=dp) :: Youngs_Modulus(n), Shear_Modulus(n), Area(n), Density(n) + REAL(KIND=dp) :: Torsional_Constant(n) + REAL(KIND=dp) :: Area_Moment_2(n), Area_Moment_3(n) + REAL(KIND=dp) :: Mass_Inertia_Moment(n) + REAL(KIND=dp) :: Load(3,n), f(3) + REAL(KIND=dp) :: PrevSolVec(6*nd) + REAL(KIND=dp) :: E, A, G, rho + REAL(KIND=dp) :: EA, GA, MOI, Mass_per_Length + REAL(KIND=dp) :: E_diag(3) + + REAL(KIND=dp) :: p1(3), p2(3), e1(3), e2(3), e3(3) + REAL(KIND=dp) :: L, Norm + + SAVE Nodes, LocalNodes +!------------------------------------------------------------------------------ + IF (n > 2) CALL Fatal('BeamStiffnessMatrix', & + 'Only 2-node background meshes supported currently') + + DOFs = 6 +! dim = CoordinateSystemDimension() + + CALL GetElementNodes(Nodes) + + Mass = 0.0_dp + Stiff = 0.0_dp + Damp = 0.0_dp + Force = 0.0_dp + + IF (PRESENT(RHSForce)) RHSForce = 0.0d0 + IF (PRESENT(LargeDeflection)) THEN + NonlinAssembly = LargeDeflection + ELSE + NonlinAssembly = .FALSE. + END IF + IF (NonlinAssembly) THEN + IF (.NOT. PRESENT(LocalSol)) CALL Fatal('BeamStiffnessMatrix', & + 'Previous solution iterate needed') + DO i=1,DOFs + PrevSolVec(i:DOFs*(nd-nb):DOFs) = LocalSol(i,1:(nd-nb)) + END DO + END IF + + + BodyForce => GetBodyForce() + IF ( ASSOCIATED(BodyForce) ) THEN + ! + ! Force components refer to the basis of the global frame: + ! + Load(1,1:n) = GetReal(BodyForce, 'Body Force 1', Found) + Load(2,1:n) = GetReal(BodyForce, 'Body Force 2', Found) + Load(3,1:n) = GetReal(BodyForce, 'Body Force 3', Found) + ELSE + Load = 0.0_dp + END IF + + Material => GetMaterial() + Youngs_Modulus(1:n) = GetReal(Material, 'Youngs Modulus', Found) + Shear_Modulus(1:n) = GetReal(Material, 'Shear Modulus', Found) + Area(1:n) = GetReal(Material, 'Cross Section Area', Found) + Torsional_Constant(1:n) = GetReal(Material, 'Torsional Constant', Found) + Area_Moment_2(1:n) = GetReal(Material, 'Second Moment of Area 2', Found) + Area_Moment_3(1:n) = GetReal(Material, 'Second Moment of Area 3', Found) + + IF (MassAssembly) THEN + Density(1:n) = GetReal(Material, 'Density', Found) + END IF + + ! + ! Compute the tangent vector e1 to the beam axis: + ! + p1(1) = Nodes % x(1) + p1(2) = Nodes % y(1) + p1(3) = Nodes % z(1) + p2(1) = Nodes % x(2) + p2(2) = Nodes % y(2) + p2(3) = Nodes % z(2) + e1 = p2 - p1 + L = SQRT(SUM(e1(:)**2)) + e1 = 1.0_dp/L * e1 + ! + ! Cross section parameters are given with respect to a local frame. + ! Determine its orientation: + ! + ArrayPtr => ListGetConstRealArray(Material, 'Director', Found) + IF (Found) THEN + e3 = 0.0d0 + DO i=1,SIZE(ArrayPtr,1) + e3(i) = ArrayPtr(i,1) + END DO + Norm = SQRT(SUM(e3(:)**2)) + e3 = 1.0_dp/Norm * e3 + IF (ABS(DOT_PRODUCT(e1,e3)) > 100.0_dp * AEPS) CALL Fatal('BeamStiffnessMatrix', & + 'Director should be orthogonal to the beam axis') + e2 = CrossProduct(e3, e1) + ELSE + ArrayPtr => ListGetConstRealArray(Material, 'Principal Direction 2', Found) + IF (Found) THEN + e2 = 0.0d0 + DO i=1,SIZE(ArrayPtr,1) + e2(i) = ArrayPtr(i,1) + END DO + Norm = SQRT(SUM(e2(:)**2)) + e2 = 1.0_dp/Norm * e2 + ELSE + e2 = -ZBasis + END IF + IF (ABS(DOT_PRODUCT(e1,e2)) > 100.0_dp * AEPS) CALL Fatal('BeamStiffnessMatrix', & + 'Principal Direction 2 should be orthogonal to the beam axis') + e3 = CrossProduct(e1, e2) + END IF + + + ! + ! Allocate an additional variable so as to write nodes data with respect to + ! the local frame. + ! + IF (.NOT. ASSOCIATED(LocalNodes % x)) THEN + ALLOCATE(LocalNodes % x(n), LocalNodes % y(n), LocalNodes % z(n) ) + LocalNodes % NumberOfNodes = n + LocalNodes % y(:) = 0.0_dp + LocalNodes % z(:) = 0.0_dp + END IF + LocalNodes % x(1) = 0.0d0 + LocalNodes % x(2) = L + + !----------------------- + ! Numerical integration: + !----------------------- + IF (.NOT. IsPElement(Element) .AND. nd > n) THEN + IP = GaussPoints(Element, 3) + ELSE + IP = GaussPoints(Element) + END IF + + DO t=1,IP % n + !-------------------------------------------------------------- + ! Basis function values & derivatives at the integration point: + !-------------------------------------------------------------- + stat = ElementInfo(Element, LocalNodes, IP % U(t), IP % V(t), & + IP % W(t), detJ, Basis, dBasis) + + ! Create a bubble if the element is the standard 2-node element: + IF (.NOT. IsPElement(Element) .AND. nd > n) THEN + Basis(n+1) = Basis(1) * Basis(2) + dBasis(3,:) = dBasis(1,:) * Basis(2) + Basis(1) * dBasis(2,:) + END IF + + !------------------------------------------ + ! The model data at the integration point: + !------------------------------------------ + f(1) = SUM(Basis(1:n) * Load(1,1:n)) + f(2) = SUM(Basis(1:n) * Load(2,1:n)) + f(3) = SUM(Basis(1:n) * Load(3,1:n)) + + ! TO DO: Add option to give the applied moment load + + E = SUM(Basis(1:n) * Youngs_Modulus(1:n)) + G = SUM(Basis(1:n) * Shear_Modulus(1:n)) + A = SUM(Basis(1:n) * Area(1:n)) + + E_diag(1) = G * SUM(Basis(1:n) * Torsional_Constant(1:n)) + E_diag(2) = E * SUM(Basis(1:n) * Area_Moment_2(1:n)) + E_diag(3) = E * SUM(Basis(1:n) * Area_Moment_3(1:n)) + + IF (MassAssembly) THEN + rho = SUM(Basis(1:n) * Density(1:n)) + MOI = rho/E * sqrt(E_diag(2)**2 + E_diag(3)**2) + Mass_per_Length = rho * A + END IF + + GA = G*A + EA = E*A + + ! TO DO: Add option to give shear correction factors + + Weight = IP % s(t) * DetJ + + DO p=1,nd + p0 = (p-1)*DOFs + DO q=1,nd + q0 = (q-1)*DOFs + StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) + MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) + ! + ! (Du',v'): + ! + StiffBlock(1,1) = StiffBlock(1,1) + & + EA * dBasis(q,1) * dBasis(p,1) * Weight + StiffBlock(2,2) = StiffBlock(2,2) + & + GA * dBasis(q,1) * dBasis(p,1) * Weight + StiffBlock(3,3) = StiffBlock(3,3) + & + GA * dBasis(q,1) * dBasis(p,1) * Weight + + IF (MassAssembly) THEN + MassBlock(1,1) = MassBlock(1,1) + & + Mass_per_Length * Basis(q) * Basis(p) * Weight + MassBlock(2,2) = MassBlock(2,2) + & + Mass_per_Length * Basis(q) * Basis(p) * Weight + MassBlock(3,3) = MassBlock(3,3) + & + Mass_per_Length * Basis(q) * Basis(p) * Weight + END IF + + IF (q > n) CYCLE + ! + ! -(D theta x t,v'): + ! + StiffBlock(2,6) = StiffBlock(2,6) - & + GA * Basis(q) * dBasis(p,1) * Weight + StiffBlock(3,5) = StiffBlock(3,5) + & + GA * Basis(q) * dBasis(p,1) * Weight + END DO + + Force(p0+1) = Force(p0+1) + Weight * DOT_PRODUCT(f,e1)* Basis(p) + Force(p0+2) = Force(p0+2) + Weight * DOT_PRODUCT(f,e2)* Basis(p) + Force(p0+3) = Force(p0+3) + Weight * DOT_PRODUCT(f,e3)* Basis(p) + + IF (p > n) CYCLE + + DO q=1,nd + q0 = (q-1)*DOFs + StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) + MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) + ! + ! -(D u',psi x t): + ! + StiffBlock(5,3) = StiffBlock(5,3) + & + GA * Basis(p) * dBasis(q,1) * Weight + StiffBlock(6,2) = StiffBlock(6,2) - & + GA * Basis(p) * dBasis(q,1) * Weight + + IF (q > n) CYCLE + + ! + ! (E theta',psi') + (D theta x t,psi x t): + ! + StiffBlock(4,4) = StiffBlock(4,4) + & + E_diag(1) * dBasis(q,1) * dBasis(p,1) * Weight + StiffBlock(5,5) = StiffBlock(5,5) + & + E_diag(2) * dBasis(q,1) * dBasis(p,1) * Weight + & + GA * Basis(p) * Basis(q) * Weight + StiffBlock(6,6) = StiffBlock(6,6) + & + E_diag(3) * dBasis(q,1) * dBasis(p,1) * Weight + & + GA * Basis(p) * Basis(q) * Weight + + IF (MassAssembly) THEN + MassBlock(4,4) = MassBlock(4,4) + MOI * Basis(q) * Basis(p) * Weight + MassBlock(5,5) = MassBlock(5,5) + rho/E * E_diag(2) * & + Basis(q) * Basis(p) * Weight + MassBlock(6,6) = MassBlock(6,6) + rho/E * E_diag(3) * & + Basis(q) * Basis(p) * Weight + END IF + + END DO + END DO + END DO + + CALL BeamCondensate(nd-nb, nb, DOFs, 3, Stiff, Force) + + IF (PRESENT(CombineWithShell)) THEN + IF (CombineWithShell) THEN + ! + ! Switch to rotation variables which conform with the rotated moments - M x d: + ! + R = 0.0d0 + DO i=1,nd-nb + i0 = (i-1)*DOFs + R(i0+1,i0+1) = 1.0d0 + R(i0+2,i0+2) = 1.0d0 + R(i0+3,i0+3) = 1.0d0 + R(i0+4,i0+5) = 1.0d0 + R(i0+5,i0+4) = -1.0d0 + R(i0+6,i0+6) = 1.0d0 + END DO + DOFs = (nd-nb)*DOFs + Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & + MATMUL(Stiff(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) + Force(1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)),Force(1:DOFs)) + + IF (MassAssembly) & + Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & + MATMUL(Mass(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) + + ! + ! The moment around the director is not compatible with the shell model. + ! Remove its contribution: + ! + DO p=1,nd-nb + Stiff(6*p,:) = 0.0d0 + Stiff(:,6*p) = 0.0d0 + Stiff(6*p,6*p) = 0.0d0 + Force(6*p) = 0.0d0 + Mass(6*p,:) = 0.0d0 + Mass(:,6*p) = 0.0d0 + END DO + END IF + END IF + + ! + ! Build the transformation matrix in order to switch to the global DOFs + ! + DOFs = 6 + R = 0.0d0 + RBlock(1,1:3) = e1(1:3) + RBlock(2,1:3) = e2(1:3) + RBlock(3,1:3) = e3(1:3) + DO i=1,nd-nb + i0 = (i-1)*DOFs + R(i0+1:i0+3,i0+1:i0+3) = RBlock(1:3,1:3) + R(i0+4:i0+6,i0+4:i0+6) = RBlock(1:3,1:3) + END DO + + !------------------------------------------------------- + ! Transform to the global DOFs: + !------------------------------------------------------- + DOFs = (nd-nb)*DOFs + Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & + MATMUL(Stiff(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) + Force(1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)),Force(1:DOFs)) + + IF (PRESENT(RHSForce)) RHSForce(1:DOFs) = Force(1:DOFs) + IF (NonlinAssembly) Force(1:DOFs) = Force(1:DOFs) - & + MATMUL(Stiff(1:DOFs,1:DOFs), PrevSolVec(1:DOFs)) + + IF (MassAssembly) THEN + Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & + MATMUL(Mass(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) + IF (TransientSimulation) THEN + CALL Default2ndOrderTime(Mass, Damp, Stiff, Force) + ELSE IF (PRESENT(HarmonicAssembly)) THEN + IF (HarmonicAssembly) CALL DefaultUpdateMass(Mass) + END IF + END IF + + CALL DefaultUpdateEquations(Stiff, Force) +!------------------------------------------------------------------------------ + END SUBROUTINE BeamStiffnessMatrix +!------------------------------------------------------------------------------ + + +!------------------------------------------------------------------------------ + SUBROUTINE BeamCondensate(n, nb, dofs, dim, K, F, F1 ) +!------------------------------------------------------------------------------ + USE LinearAlgebra + IMPLICIT NONE + INTEGER, INTENT(IN) :: n ! Nodes after condensation + INTEGER, INTENT(IN) :: nb ! The number of bubble basis functions + INTEGER, INTENT(IN) :: dofs ! DOFs per node + INTEGER, INTENT(IN) :: dim ! The first dim fields have bubbles + REAL(KIND=dp), INTENT(INOUT) :: K(:,:) ! The stiffness matrix + REAL(KIND=dp), INTENT(INOUT) :: F(:) ! The RHS vector + REAL(KIND=dp), OPTIONAL, INTENT(INOUT) :: F1(:) ! Some other RHS vector +!------------------------------------------------------------------------------ + REAL(KIND=dp) :: Kbl(nb*dim,n*dofs), Kbb(nb*dim,nb*dim), Fb(nb*dim) + REAL(KIND=dp) :: Klb(n*dofs,nb*dim) + + INTEGER :: i, m, p, Cdofs(dofs*n), Bdofs(dim*nb) +!------------------------------------------------------------------------------ + + Cdofs(1:n*dofs) = (/ (i, i=1,n*dofs) /) + + m = 0 + DO p = 1,nb + DO i = 1,dim + m = m + 1 + Bdofs(m) = dofs*(n+p-1) + i + END DO + END DO + + Kbb = K(Bdofs,Bdofs) + Kbl = K(Bdofs,Cdofs) + Klb = K(Cdofs,Bdofs) + Fb = F(Bdofs) + + CALL InvertMatrix( Kbb,nb*dim ) + + F(1:dofs*n) = F(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) + K(1:dofs*n,1:dofs*n) = & + K(1:dofs*n,1:dofs*n) - MATMUL( Klb, MATMUL( Kbb,Kbl ) ) + + IF (PRESENT(F1)) THEN + Fb = F1(Bdofs) + F1(1:dofs*n) = F1(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) + END IF +!------------------------------------------------------------------------------ + END SUBROUTINE BeamCondensate +!------------------------------------------------------------------------------ + +!------------------------------------------------------------------------------ +!> Perform the operation +!> +!> A = A + C' * B * C * s +!> +!> with +!> +!> Size( A ) = n x n +!> Size( B ) = m x m +!> Size( C ) = m x n +!------------------------------------------------------------------------------ + SUBROUTINE StrainEnergyDensity(A, B, C, m, n, s) +!------------------------------------------------------------------------------ + IMPLICIT NONE + REAL(KIND=dp), INTENT(INOUT) :: A(:,:) + REAL(KIND=dp), INTENT(IN) :: B(:,:), C(:,:) + INTEGER, INTENT(IN) :: m, n + REAL(KIND=dp), INTENT(IN) :: s +!------------------------------------------------------------------------------ + A(1:n,1:n) = A(1:n,1:n) + s * MATMUL(TRANSPOSE(C(1:m,1:n)),MATMUL(B(1:m,1:m),C(1:m,1:n))) +!------------------------------------------------------------------------------ + END SUBROUTINE StrainEnergyDensity +!------------------------------------------------------------------------------ + + +!------------------------------------------------------------------------------ + SUBROUTINE Jacobi3(Jmat, invJ, detJ, x, y) +!------------------------------------------------------------------------------ + IMPLICIT NONE + REAL(KIND=dp), INTENT(OUT) :: Jmat(:,:), invJ(:,:), detJ + REAL(KIND=dp), INTENT(IN) :: x(:), y(:) +!------------------------------------------------------------------------------ + Jmat(1,1) = x(2)-x(1) + Jmat(2,1) = x(3)-x(1) + Jmat(1,2) = y(2)-y(1) + Jmat(2,2) = y(3)-y(1) + + detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) + + invJ(1,1) = Jmat(2,2)/detJ + invJ(2,2) = Jmat(1,1)/detJ + invJ(1,2) = -Jmat(1,2)/detJ + invJ(2,1) = -Jmat(2,1)/detJ +!------------------------------------------------------------------------------ + END SUBROUTINE Jacobi3 +!------------------------------------------------------------------------------ + +!------------------------------------------------------------------------------ + SUBROUTINE Jacobi4(Jmat, invJ, detJ, xi, eta, x, y) +!------------------------------------------------------------------------------ + IMPLICIT NONE + REAL(KIND=dp), INTENT(OUT) :: Jmat(:,:), invJ(:,:), detJ + REAL(KIND=dp), INTENT(IN) :: xi, eta, x(:), y(:) +!------------------------------------------------------------------------------ + REAL(KIND=dp) :: dNdxi(4), dNdeta(4) + INTEGER :: i +!------------------------------------------------------------------------------ + dNdxi(1) = -(1-eta)/4.0d0 + dNdxi(2) = (1-eta)/4.0d0 + dNdxi(3) = (1+eta)/4.0d0 + dNdxi(4) = -(1+eta)/4.0d0 + dNdeta(1) = -(1-xi)/4.0d0 + dNdeta(2) = -(1+xi)/4.0d0 + dNdeta(3) = (1+xi)/4.0d0 + dNdeta(4) = (1-xi)/4.0d0 + + Jmat = 0.0d0 + DO i=1,4 + Jmat(1,1) = Jmat(1,1) + dNdxi(i)*x(i) + Jmat(1,2) = Jmat(1,2) + dNdxi(i)*y(i) + Jmat(2,1) = Jmat(2,1) + dNdeta(i)*x(i) + Jmat(2,2) = Jmat(2,2) + dNdeta(i)*y(i) + END DO + + detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) + + invJ(1,1) = Jmat(2,2)/detJ + invJ(2,2) = Jmat(1,1)/detJ + invJ(1,2) = -Jmat(1,2)/detJ + invJ(2,1) = -Jmat(2,1)/detJ +!------------------------------------------------------------------------------ + END SUBROUTINE Jacobi4 +!------------------------------------------------------------------------------ + +!------------------------------------------------------------------------------ + SUBROUTINE ShearCorrectionFactor(Kappa, Thickness, x, y, n, StabParam) +!------------------------------------------------------------------------------ + REAL(KIND=dp), INTENT(OUT) :: Kappa + REAL(KIND=dp), INTENT(IN) :: Thickness, x(:), y(:) + INTEGER, INTENT(IN) :: n + REAL(KIND=dp), OPTIONAL, INTENT(IN) :: StabParam +!------------------------------------------------------------------------------ + REAL(KIND=dp) :: x21,x32,x43,x13,x14,y21,y32,y43,y13,y14, & + l21,l32,l43,l13,l14,alpha,h + REAL(KIND=dp) :: StabPar +!------------------------------------------------------------------------------ + IF (PRESENT(StabParam)) THEN + StabPar = StabParam + ELSE + StabPar = 1.0d0 + END IF + + Kappa = 1.0d0 + SELECT CASE(n) + CASE(3) + alpha = 0.20d0 * StabPar + x21 = x(2)-x(1) + x32 = x(3)-x(2) + x13 = x(1)-x(1) + y21 = y(2)-y(1) + y32 = y(3)-y(2) + y13 = y(1)-y(1) + l21 = SQRT(x21**2 + y21**2) + l32 = SQRT(x32**2 + y32**2) + l13 = SQRT(x13**2 + y13**2) + h = MAX(l21,l32,l13) + Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) + CASE(4) + alpha = 0.10d0 * StabPar + x21 = x(2)-x(1) + x32 = x(3)-x(2) + x43 = x(4)-x(3) + x14 = x(1)-x(4) + y21 = y(2)-y(1) + y32 = y(3)-y(2) + y43 = y(4)-y(3) + y14 = y(1)-y(4) + l21 = SQRT(x21**2 + y21**2) + l32 = SQRT(x32**2 + y32**2) + l43 = SQRT(x43**2 + y43**2) + l14 = SQRT(x14**2 + y14**2) + h = MAX(l21,l32,l43,l14) + Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) + CASE DEFAULT + CALL Fatal('ShearCorrectionFactor','Illegal number of nodes for Smitc elements: '//TRIM(I2S(n))) + END SELECT +!------------------------------------------------------------------------------ + END SUBROUTINE ShearCorrectionFactor +!------------------------------------------------------------------------------ + +!------------------------------------------------------------------------------ + SUBROUTINE IsotropicElasticity(Ematrix, Gmatrix, Poisson, Young, Thickness,& + Basis, n) +!------------------------------------------------------------------------------ + REAL(KIND=dp) :: Ematrix(:,:), Gmatrix(:,:), Basis(:) + REAL(KIND=dp) :: Poisson(:), Young(:), Thickness(:) + REAL(KIND=dp) :: Euvw, Puvw, Guvw, Tuvw + INTEGER :: n +!------------------------------------------------------------------------------ + Euvw = SUM( Young(1:n)*Basis(1:n) ) + Puvw = SUM( Poisson(1:n)*Basis(1:n) ) + Tuvw = SUM( Thickness(1:n)*Basis(1:n) ) + Guvw = Euvw/(2.0d0*(1.0d0 + Puvw)) + + Ematrix = 0.0d0 + Ematrix(1,1) = 1.0d0 + Ematrix(1,2) = Puvw + Ematrix(2,1) = Puvw + Ematrix(2,2) = 1.0d0 + Ematrix(3,3) = (1.0d0-Puvw)/2.0d0 + + Ematrix = Ematrix* Euvw * (Tuvw**3) / (12.0d0*(1.0d0-Puvw**2)) + + Gmatrix = 0.0d0 + Gmatrix(1,1) = Guvw*Tuvw + Gmatrix(2,2) = Guvw*Tuvw +!------------------------------------------------------------------------------ + END SUBROUTINE IsotropicElasticity +!------------------------------------------------------------------------------ + + +END MODULE SolidMechanicsUtils + diff --git a/fem/src/Solver.F90 b/fem/src/Solver.F90 index 1f2ea6f2ae..ca87b630a0 100644 --- a/fem/src/Solver.F90 +++ b/fem/src/Solver.F90 @@ -62,8 +62,10 @@ PROGRAM Solver CPUTime()-CT, RealTime()-RT DateStr = FormatDate() WRITE( *,'(A,A)' ) 'ELMER SOLVER FINISHED AT: ', TRIM(DateStr) + CALL FLUSH(6) END IF END IF + END PROGRAM Solver ! ****************************************************************************** diff --git a/fem/src/SolverUtils.F90 b/fem/src/SolverUtils.F90 index 8d0d8520c1..6c8b51252d 100644 --- a/fem/src/SolverUtils.F90 +++ b/fem/src/SolverUtils.F90 @@ -2826,7 +2826,7 @@ SUBROUTINE CalculateMortarDistance() LOGICAL :: LinearContactGap, DebugNormals - CALL Info(Caller,'Computing distance between mortar boundaries',Level=14) + CALL Info('CalculateMortarDistance','Computing distance between mortar boundaries',Level=14) DispVals => Solver % Variable % Values IF( .NOT. ASSOCIATED( DispVals ) ) THEN @@ -2842,7 +2842,7 @@ SUBROUTINE CalculateMortarDistance() ELSE PrevDispVals => Solver % Variable % PrevValues(:,3) END IF - IF(.NOT. ASSOCIATED( PrevDispVals ) ) CALL Fatal(Caller,& + IF(.NOT. ASSOCIATED( PrevDispVals ) ) CALL Fatal('CalculateMortarDistance',& 'Previous displacement field required!') END IF @@ -3211,7 +3211,7 @@ SUBROUTINE CalculateMortarDistance() END DO CASE DEFAULT - CALL Fatal(Caller,'Implement linear gaps for: '//TRIM(I2S(ElemCode))) + CALL Fatal('CalculateMortarDistance','Implement linear gaps for: '//TRIM(I2S(ElemCode))) END SELECT END DO END IF @@ -3808,7 +3808,7 @@ SUBROUTINE StickCoefficientSet() INTEGER :: i,j,k,ind,IndN, IndT1, IndT2 LOGICAL :: Found - CALL Info('StickCoefficienttSet','Setting the stick coefficient entry for tangent components at stick',Level=10) + CALL Info('StickCoefficientSet','Setting the stick coefficient entry for tangent components at stick',Level=10) ! Determine now whether we have contact or not DO i = 1,Projector % NumberOfRows @@ -3965,7 +3965,7 @@ SUBROUTINE QuadraticContactSet() END IF CASE DEFAULT - CALL Fatal('NormalContactSet','Cannot deal with element: '//TRIM(I2S(elemcode))) + CALL Fatal(Caller,'Cannot deal with element: '//TRIM(I2S(elemcode))) END SELECT END DO @@ -4403,7 +4403,7 @@ FUNCTION DirichletDofsRange( Solver, Oper ) RESULT ( val ) ELSE IF( Oper == 'max' ) THEN OperNo = 2 ELSE - CALL Fatal('DirichletDofRange','Unknown operator: '//TRIM(Oper)) + CALL Fatal('DirichletDofsRange','Unknown operator: '//TRIM(Oper)) END IF END IF @@ -4931,7 +4931,7 @@ SUBROUTINE SetDirichletBoundaries( Model, A, b, Name, DOF, NDOFs, Perm, & ! In the first time add the found nodes to the list structure IF ( NOFNodesFound > 0 ) THEN DO i=1,NOFNodesFound - CALL Info('SetNodalLoads','Target Nodes('//TRIM(I2S(i))//& + CALL Info(Caller, 'Target Nodes('//TRIM(I2S(i))//& ') = '//TRIM(I2S(IndNodes(i))),Level=7) END DO CALL ListAddIntegerArray( ValueList,'Target Nodes', & @@ -5190,7 +5190,7 @@ SUBROUTINE SetDirichletBoundaries( Model, A, b, Name, DOF, NDOFs, Perm, & ! Move the list matrix because of its flexibility IF( NeedListMatrix ) THEN CALL Info(Caller,'Using List maxtrix to set constant constraints',Level=8) - CALL Info('SetDircihletBoundaries','Original matrix non-zeros: '& + CALL Info(Caller,'Original matrix non-zeros: '& //TRIM(I2S(SIZE( A % Cols ))),Level=8) IF( ASSOCIATED( A % BulkValues ) ) THEN ALLOCATE( Cols0( SIZE( A % Cols ) ), Rows0( SIZE( A % Rows ) ) ) @@ -5333,7 +5333,7 @@ SUBROUTINE SetDirichletBoundaries( Model, A, b, Name, DOF, NDOFs, Perm, & DEALLOCATE( Cols0, Rows0, BulkValues0 ) END IF - CALL Info('SetDircihletBoundaries','Modified matrix non-zeros: '& + CALL Info(Caller,'Modified matrix non-zeros: '& //TRIM(I2S(SIZE( A % Cols ))),Level=8) END IF END IF @@ -5360,7 +5360,7 @@ SUBROUTINE SetDirichletBoundaries( Model, A, b, Name, DOF, NDOFs, Perm, & ! Move the list matrix because of its flexibility IF( NeedListMatrix ) THEN CALL Info(Caller,'Using List maxtrix to set constant constraints',Level=8) - CALL Info('SetDircihletBoundaries','Original matrix non-zeros: '& + CALL Info(Caller,'Original matrix non-zeros: '& //TRIM(I2S(SIZE( A % Cols ))),Level=8) IF( ASSOCIATED( A % BulkValues ) ) THEN ALLOCATE( Cols0( SIZE( A % Cols ) ), Rows0( SIZE( A % Rows ) ) ) @@ -5465,7 +5465,7 @@ SUBROUTINE SetDirichletBoundaries( Model, A, b, Name, DOF, NDOFs, Perm, & DEALLOCATE( Cols0, Rows0, BulkValues0 ) END IF - CALL Info('SetDircihletBoundaries','Modified matrix non-zeros: '& + CALL Info(Caller,'Modified matrix non-zeros: '& //TRIM(I2S(SIZE( A % Cols ))),Level=8) END IF END IF @@ -6024,7 +6024,7 @@ SUBROUTINE SetPeriodicBoundariesPass1( Model, A, b, & IF( ASSOCIATED( Projector, & Model % Solver % MortarBCs(This) % Projector) ) THEN - CALL Info('SetPeridociBoundariesPass1','Using existing projector: '& + CALL Info('SetPeriodicBoundariesPass1','Using existing projector: '& //TRIM(I2S(This)),Level=8) RETURN END IF @@ -6041,7 +6041,7 @@ SUBROUTINE SetPeriodicBoundariesPass1( Model, A, b, & END IF END IF IF( .NOT. ASSOCIATED( MortarBC % Diag ) ) THEN - CALL Info('SetWeightedPeridocBCsPass1','Allocating projector mortar diag',Level=10) + CALL Info('SetPeriodicBoundariesPass1','Allocating projector mortar diag',Level=10) ALLOCATE( MortarBC % Diag( NDofs * Projector % NumberOfRows ) ) MortarBC % Diag = 0.0_dp ELSE @@ -6054,7 +6054,7 @@ SUBROUTINE SetPeriodicBoundariesPass1( Model, A, b, & END IF END IF IF( .NOT. ASSOCIATED( MortarBC % Rhs ) ) THEN - CALL Info('SetWeightedProjectorPass1','Allocating projector mortar rhs',Level=10) + CALL Info('SetPeriodicBoundariesPass1','Allocating projector mortar rhs',Level=10) ALLOCATE( MortarBC % Rhs( NDofs * Projector % NumberOfRows ) ) MortarBC % Rhs = 0.0_dp ELSE @@ -6069,7 +6069,7 @@ SUBROUTINE SetPeriodicBoundariesPass1( Model, A, b, & END IF END IF IF( .NOT. ASSOCIATED( MortarBC % Perm ) ) THEN - CALL Info('SetWeightedProjectorPass1','Allocating projector mortar perm',Level=10) + CALL Info('SetPeriodicBoundariesPass1','Allocating projector mortar perm',Level=10) ALLOCATE( MortarBC % Perm( SIZE( Perm ) ) ) END IF @@ -6716,7 +6716,7 @@ SUBROUTINE SetConstraintModesBoundaries( Model, A, b, & DEALLOCATE( BCPerm ) - CALL Info('SetConstraintModesBoundarues','All done',Level=10) + CALL Info('SetConstraintModesBoundaries','All done',Level=10) !------------------------------------------------------------------------------ END SUBROUTINE SetConstraintModesBoundaries @@ -6852,7 +6852,7 @@ SUBROUTINE SetNodalSources( Model, Mesh, SourceName, dofs, Perm, GotSrc, SrcVec Coeff = ParallelScalingFactor() IF(ABS(Coeff) < TINY(Coeff)) CYCLE - CALL LocalSourceAssembly(Element, n, dofs, FORCE ) + CALL LocalSourceAssembly(Element, dofs, FORCE ) DO i=1,dofs SrcVec(dofs*(Perm(Indexes)-1)+i) = SrcVec(dofs*(Perm(Indexes)-1)+i) + & @@ -6899,10 +6899,10 @@ END FUNCTION ParallelScalingFactor !------------------------------------------------------------------------------ - SUBROUTINE LocalSourceAssembly(Element, n, dofs, FORCE) + SUBROUTINE LocalSourceAssembly(Element, dofs, FORCE) !------------------------------------------------------------------------------ IMPLICIT NONE - INTEGER, INTENT(IN) :: n, dofs + INTEGER, INTENT(IN) :: dofs TYPE(Element_t), POINTER :: Element REAL(KIND=dp) :: FORCE(:,:) !------------------------------------------------------------------------------ @@ -6910,13 +6910,13 @@ SUBROUTINE LocalSourceAssembly(Element, n, dofs, FORCE) REAL(KIND=dp) :: weight, SourceAtIp, DetJ INTEGER, POINTER :: Indexes(:) LOGICAL :: Stat,Found - INTEGER :: i,j,t,m,allocstat + INTEGER :: i,j,t,m,n,allocstat TYPE(GaussIntegrationPoints_t) :: IP TYPE(Nodes_t) :: Nodes SAVE Nodes,Basis,ElemSource !------------------------------------------------------------------------------ - + ! Allocate storage if needed IF (.NOT. ALLOCATED(Basis)) THEN m = Mesh % MaxElementNodes @@ -6929,7 +6929,8 @@ SUBROUTINE LocalSourceAssembly(Element, n, dofs, FORCE) IP = GaussPoints( Element, PReferenceElement = .FALSE.) Indexes => Element % NodeIndexes - + n = Element % Type % NumberOfNodes + Nodes % x(1:n) = Mesh % Nodes % x(Indexes) Nodes % y(1:n) = Mesh % Nodes % y(Indexes) Nodes % z(1:n) = Mesh % Nodes % z(Indexes) @@ -7266,9 +7267,12 @@ END SUBROUTINE SetElementLoads SUBROUTINE SetPointLoads(n) INTEGER :: n REAL(KIND=dp) :: Work(n) + LOGICAL :: ImaginaryLoads + CHARACTER(LEN=MAX_NAME_LEN) :: LoadNameIm IF(n<=0) RETURN - + ImaginaryLoads = ASSOCIATED(A % RHS_im) + IF ( DOF > 0 ) THEN Work(1:n) = ListGetReal( ValueList, LoadName, n, NodeIndexes, gotIt ) ELSE @@ -7278,7 +7282,7 @@ SUBROUTINE SetPointLoads(n) IF ( GotIt ) THEN DO j=1,n IF ( NodeIndexes(j) > SIZE(Perm) .OR. NodeIndexes(j) < 1 ) THEN - CALL Warn('SetNodalLoads','Invalid Node Number') + CALL Warn('SetPointLoads','Invalid Node Number') CYCLE END IF @@ -7297,6 +7301,36 @@ SUBROUTINE SetPointLoads(n) END DO END IF + IF (ImaginaryLoads) THEN + IF (DOF > 0) THEN + Work(1:n) = ListGetReal(ValueList, LoadName(1:nlen) // ' im', n, NodeIndexes, gotIt) + ELSE + CALL ListGetRealArray(ValueList, LoadName(1:nlen) // ' im', WorkA, n, NodeIndexes, gotIt) + END IF + + IF (GotIt) THEN + DO j=1,n + IF ( NodeIndexes(j) > SIZE(Perm) .OR. NodeIndexes(j) < 1 ) THEN + CALL Warn('SetPointLoads','Invalid Node Number') + CYCLE + END IF + + k = Perm(NodeIndexes(j)) + IF ( k > 0 ) THEN + IF (DOF > 0) THEN + k = NDOFs * (k-1) + DOF + A % RHS_im(k) = A % RHS_im(k) + Work(j) + ELSE + DO l=1,MIN( NDOFs, SIZE(WorkA,1) ) + k1 = NDOFs * (k-1) + l + A % RHS_im(k1) = A % RHS_im(k1) + WorkA(l,1,j) + END DO + END IF + END IF + END DO + END IF + END IF + END SUBROUTINE SetPointLoads !------------------------------------------------------------------------------ @@ -8742,7 +8776,7 @@ SUBROUTINE AverageBoundaryNormals( Model, VariableName, & IF( LhsSystem ) THEN DO i = 1, Model % NumberOfBcs IF( NtSlaveBC( i ) .AND. NtMasterBC( i ) ) THEN - CALL Warn('AverageBoundaryNormals','BC '//TRIM(I2S(i))//' is both N-T master and slave!') + CALL Warn(Caller,'BC '//TRIM(I2S(i))//' is both N-T master and slave!') END IF END DO @@ -8771,7 +8805,7 @@ SUBROUTINE AverageBoundaryNormals( Model, VariableName, & LhsConflicts = COUNT( LhsTangent .AND. RhsTangent ) IF( LhsConflicts > 0 ) THEN - CALL Warn('AverageBoundaryNormals',& + CALL Warn(Caller,& 'There are '//TRIM(I2S(LhsConflicts))//' nodes that could be both rhs and lhs!') END IF END IF @@ -8796,7 +8830,7 @@ SUBROUTINE AverageBoundaryNormals( Model, VariableName, & END DO IF( ListGetLogical( Model % Simulation,'Save Averaged Normals',Found ) ) THEN - CALL Info('AverageBoundaryNormals','Saving averaged boundary normals to variable: Averaged Normals') + CALL Info(Caller,'Saving averaged boundary normals to variable: Averaged Normals') NrmVar => VariableGet( Mesh % Variables, 'Averaged Normals' ) IF(.NOT. ASSOCIATED( NrmVar ) ) THEN @@ -11428,7 +11462,7 @@ SUBROUTINE AndersonGuess() IF( InfoActive(10) ) THEN DO i=1,m WRITE(Message,'(A,I0,A,ES12.3)') 'Beta(',i,') = ',Betas(i) - CALL Info('LinearAcceleration',Message) + CALL Info('NonLinearAcceleration',Message) END DO END IF @@ -11757,7 +11791,7 @@ SUBROUTINE CalculateEntityWeights(Model, Mesh) RETURN END IF - CALL Info('ComputeNodalWeights','Computing weights for the mesh entities',Level=6) + CALL Info('CalculateEntityWeights','Computing weights for the mesh entities',Level=6) n = Mesh % MaxElementNodes NoBC = Model % NumberOfBCs @@ -12307,7 +12341,7 @@ SUBROUTINE RowEquilibration( A, f, Parallel ) WRITE( Message, * ) 'Unscaled matrix norm: ', norm - CALL Info( 'OptimalMatrixScaling', Message, Level=5 ) + CALL Info( 'RowEquilibration', Message, Level=5 ) !------------------------------------------------------------------------------ END SUBROUTINE RowEquilibration @@ -12594,7 +12628,7 @@ SUBROUTINE CalculateLoads( Solver, Aaid, x, DOFs, UseBulkValues, NodalLoads, Nod CALL ListAddConstReal( CurrentModel % Simulation, Message, Energy_im ) WRITE( Message, * ) 'Energy Norm: ', Energy, Energy_im - CALL Info( 'SolveLinearSystem', Message, Level=5) + CALL Info( 'CalculateLoads', Message, Level=5) ELSE DO i=1,Aaid % NumberOfRows IF ( ParEnv % Pes>1 ) THEN @@ -12610,7 +12644,7 @@ SUBROUTINE CalculateLoads( Solver, Aaid, x, DOFs, UseBulkValues, NodalLoads, Nod CALL ListAddConstReal( CurrentModel % Simulation, Message, Energy ) WRITE( Message, * ) 'Energy Norm: ', Energy - CALL Info( 'SolveLinearSystem', Message, Level=5) + CALL Info( 'CalculateLoads', Message, Level=5) END IF END IF @@ -12945,22 +12979,22 @@ SUBROUTINE BCLoadsComputation( Solver ) CALL Fatal('BCLoadsComputation','We should have the boundary matrix!') END IF - CALL Info('CalculateBCLoads','Computing boundary loads',Level=6) + CALL Info('BCLoadsComputation','Computing boundary loads',Level=6) IF( BCMat % FORMAT == MATRIX_LIST ) THEN CALL List_ToCRSMatrix( BCMat ) - CALL Info('CalculateBCLoads','Matrix format changed to CRS',Level=8) + CALL Info('BCLoadsComputation','Matrix format changed to CRS',Level=8) END IF Name = TRIM(Solver % Variable % Name)//' BCLoads' BCVar => VariableGet( Solver % Mesh % Variables, TRIM( Name ) ) IF(.NOT. ASSOCIATED( BCVar ) ) THEN - CALL Fatal('CalculateBCLoads','Variable not present: '//TRIM(Name)) + CALL Fatal('BCLoadsComputation','Variable not present: '//TRIM(Name)) END IF CALL MatrixVectorMultiply( BCMat, Solver % Variable % Values, BCVar % Values ) BCVar % Values = BCVar % Values - BCMat % rhs - CALL Info('CalculateBCLoads','All done',Level=12) + CALL Info('BCLoadsComputation','All done',Level=12) END SUBROUTINE BCLoadsComputation @@ -14762,7 +14796,7 @@ SUBROUTINE SolveHarmonicSystem( G, Solver ) INTEGER :: Nfrequency TYPE(ValueList_t), POINTER :: BC - CALL Info( 'HarmonicSolve', 'Solving initially transient style system as harmonic one', Level=5) + CALL Info( 'SolveHarmonicSystem', 'Solving initially transient style system as harmonic one', Level=5) n = Solver % Matrix % NumberofRows DOFs = Solver % Variable % DOFs * 2 @@ -14822,7 +14856,7 @@ SUBROUTINE SolveHarmonicSystem( G, Solver ) ELSE Frequency = ListGetAngularFrequency( Solver % Values, Found ) / (2*PI) IF( .NOT. Found ) THEN - CALL Fatal( 'AddEquation', '> Frequency < must be given for harmonic analysis.' ) + CALL Fatal( 'SolveHarmonicSystem', '> Frequency < must be given for harmonic analysis.' ) END IF Nfrequency = 1 @@ -14841,7 +14875,7 @@ SUBROUTINE SolveHarmonicSystem( G, Solver ) ELSE WRITE( Message, '(a,e12.3)' ) 'Frequency value: ', frequency END IF - CALL Info( 'HarmonicSolve', Message, Level=4 ) + CALL Info( 'SolveHarmonicSystem', Message, Level=4 ) omega = 2 * PI * Frequency DO k=1,n @@ -17461,6 +17495,7 @@ FUNCTION GetElementalDirectorInt(Mesh, Element, & IF( PRESENT( ElementNodes ) ) THEN Normal = NormalVector( Element, ElementNodes, Check = .TRUE. ) ELSE + n = Element % Type % NumberOfNodes Nodes % x(1:n) = Mesh % Nodes % x(Element % NodeIndexes) Nodes % y(1:n) = Mesh % Nodes % y(Element % NodeIndexes) Nodes % z(1:n) = Mesh % Nodes % z(Element % NodeIndexes) @@ -17514,7 +17549,7 @@ END FUNCTION GetElementalDirectorInt !> tied up with the value of the first entry in the "Block Solvers" array. !------------------------------------------------------------------------------ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & - IsSolid, IsPlate, IsShell, IsBeam ) + IsSolid, IsPlate, IsShell, IsBeam, DrillingDOFs) !------------------------------------------------------------------------------ TYPE(Solver_t) :: Solver !< The leading solver defining block structure TYPE(Variable_t), POINTER :: FVar !< "Slave" structure variable @@ -17524,6 +17559,7 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & TYPE(Matrix_t), POINTER :: A_fs !< (2,1)-block for interaction TYPE(Matrix_t), POINTER :: A_sf !< (1,2)-block for interaction LOGICAL :: IsSolid, IsPlate, IsShell, IsBeam !< The type of the slave variable + LOGICAL :: DrillingDOFs !< Use drilling rotation formulation for shells !------------------------------------------------------------------------------ TYPE(Mesh_t), POINTER :: Mesh LOGICAL, POINTER :: ConstrainedF(:), ConstrainedS(:) @@ -17580,13 +17616,13 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & IF( ASSOCIATED( A_s % MassValues ) ) THEN DoMass = .TRUE. ELSE - CALL Warn(Caller,'Both solid and shell should have MassValues!') + CALL Warn(Caller,'Both models should have MassValues!') END IF END IF DoDamp = ASSOCIATED( A_f % DampValues ) IF( DoDamp ) THEN - CALL Warn(Caller,'Damping matrix values at shell interface will be dropped!') + CALL Warn(Caller,'Damping matrix values at a coupling interface will be dropped!') END IF ! This is still under development and not used for anything @@ -17602,9 +17638,10 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & INTEGER, ALLOCATABLE :: NodeHits(:), InterfacePerm(:), InterfaceElems(:,:) INTEGER :: InterfaceN, hits INTEGER :: p,lf,ls,ii,jj,n,m,t + INTEGER :: NormalDir REAL(KIND=dp), POINTER :: Director(:) REAL(KIND=dp), POINTER :: Basis(:), dBasisdx(:,:) - REAL(KIND=dp), ALLOCATABLE :: A_f0(:) + REAL(KIND=dp), ALLOCATABLE :: A_f0(:), rhs0(:), Mass0(:) REAL(KIND=dp) :: u,v,w,weight,detJ,val REAL(KIND=dp) :: x, y, z @@ -17618,13 +17655,22 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & ! Memorize the original values ALLOCATE( A_f0( SIZE( A_f % Values ) ) ) A_f0 = A_f % Values + IF (DrillingDOFs) THEN + ALLOCATE(rhs0(SIZE(A_f % rhs))) + rhs0 = A_f % rhs + IF (DoMass) THEN + ALLOCATE(Mass0(SIZE(A_f % MassValues))) + Mass0 = A_f % MassValues + END IF + END IF ALLOCATE( NodeHits( Mesh % NumberOfNodes ), InterfacePerm( Mesh % NumberOfNodes ) ) NodeHits = 0 InterfacePerm = 0 - ! First, zero the rows related to directional derivative dofs, + ! First, in the basic case zero the rows related to directional derivative dofs, ! i.e. the components 4,5,6. "s" refers to solid and "f" to shell. + ! InterfaceN = 0 DO i=1,Mesh % NumberOfNodes jf = FPerm(i) @@ -17773,44 +17819,123 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & !PRINT *,'Director:',ShellElement % ElementIndex,jj,Director - DO lf = 4, 6 + + DO lf = 4, 6 kf = fdofs*(jf-1)+lf IF( ConstrainedF(kf) ) CYCLE - DO ls = 1, dim + IF (DrillingDOFs) THEN ! - ! Directional derivative dofs of the shell equations: - ! We try to enforce the condition d_{i+3}=-<(grad u)n,e_i> - ! where i=1,2,3; i+3=lf, n is director, e_i is unit vector, and - ! u is the displacement field of the solid. - DO p=1,n - js = SPerm(Indexes(p)) - ks = sdofs*(js-1)+lf-3 - val = Director(ls) * dBasisdx(p,ls) - - CALL AddToMatrixElement(A_fs,kf,ks,weight*val) + ! In the case of drilling rotation formulation, the tangential components + ! trace of the global rotations ROT is related to the directional derivative + ! of the displacement field u by -Du[d] x d = d x ROT x d. This implementation + ! is limited to cases where the director is aligned with one of the global + ! coordinate axes. + ! + NormalDir = 0 + IF (ABS(1.0_dp - ABS(Director(1))) < 1.0d-5) THEN + NormalDir = 1 + ELSE IF (ABS(1.0_dp - ABS(Director(2))) < 1.0d-5) THEN + NormalDir = 2 + ELSE IF (ABS(1.0_dp - ABS(Director(3))) < 1.0d-5) THEN + NormalDir = 3 + END IF + IF (NormalDir == 0) CALL Fatal(Caller, & + 'Coupling with drilling rotation formulation needs an axis-aligned director') + + IF ((lf-3) /= NormalDir) THEN + + DO p = 1,n + js = SPerm(Indexes(p)) + + IF (NormalDir == 1) THEN + SELECT CASE(lf) + CASE(5) + ks = sdofs*(js-1)+3 + val = dBasisdx(p,1) + CASE(6) + ks = sdofs*(js-1)+2 + val = -dBasisdx(p,1) + END SELECT + ELSE IF (NormalDir == 2) THEN + SELECT CASE(lf) + CASE(4) + ks = sdofs*(js-1)+3 + val = -dBasisdx(p,2) + CASE(6) + ks = sdofs*(js-1)+1 + val = dBasisdx(p,2) + END SELECT + ELSE IF (NormalDir == 3) THEN + SELECT CASE(lf) + CASE(4) + ks = sdofs*(js-1)+2 + val = dBasisdx(p,3) + CASE(5) + ks = sdofs*(js-1)+1 + val = -dBasisdx(p,3) + END SELECT + END IF - ! Here the idea is to distribute the implicit moments of the shell solver - ! to forces for the solid solver. So even though the stiffness matrix related to the - ! directional derivatives is nullified, the forces are not forgotten. - ! This part may be thought of as being based on two (Råback's) conjectures: - ! in the first place the Lagrange variable formulation should bring us to a symmetric - ! coefficient matrix and the values of Lagrange variables can be estimated as nodal - ! reactions obtained by performing a matrix-vector product. + CALL AddToMatrixElement(A_fs,kf,ks,weight*val) + + DO k=A_f % Rows(kf),A_f % Rows(kf+1)-1 + CALL AddToMatrixElement(A_sf,ks,A_f % Cols(k),-weight*val*A_f0(k)) + END DO + END DO + + ELSE ! - ! Note that no attempt is currently made to transfer external moment - ! loads of the shell model to loads of the coupled model. Likewise - ! rotational inertia terms of the shell model are not transformed - ! to inertia terms of the coupled model. Neglecting the rotational - ! inertia might be acceptable in many cases. + ! Return one row of deleted values to the shell matrix ! - ! Note that the minus sign of the entries is correct here: DO k=A_f % Rows(kf),A_f % Rows(kf+1)-1 - CALL AddToMatrixElement(A_sf,ks,A_f % Cols(k),-weight*val*A_f0(k)) + A_f % Values(k) = A_f0(k) + IF (DoMass) A_f % MassValues(k) = Mass0(k) + END DO + + A_f % rhs(kf) = rhs0(kf) + + ! TO DO: Return also damp values if used + + END IF + + ELSE + ! + ! Directional derivative dofs D_{i+3} of the shell equations: + ! We try to enforce the condition D_{i+3}=-<(grad u)d,e_i> + ! where i=1,2,3; i+3=lf, d is director, e_i is unit vector, and + ! u is the displacement field of the solid. + ! + DO p = 1, n + js = SPerm(Indexes(p)) + ks = sdofs*(js-1)+lf-3 + DO ls = 1, dim + val = Director(ls) * dBasisdx(p,ls) + + CALL AddToMatrixElement(A_fs,kf,ks,weight*val) + + ! Here the idea is to distribute the implicit moments of the shell solver + ! to forces for the solid solver. So even though the stiffness matrix related to the + ! directional derivatives is nullified, the forces are not forgotten. + ! This part may be thought of as being based on two (Råback's) conjectures: + ! in the first place the Lagrange variable formulation should bring us to a symmetric + ! coefficient matrix and the values of Lagrange variables can be estimated as nodal + ! reactions obtained by performing a matrix-vector product. + ! + ! Note that no attempt is currently made to transfer external moment + ! loads of the shell model to loads of the coupled model. Likewise + ! rotational inertia terms of the shell model are not transformed + ! to inertia terms of the coupled model. Neglecting the rotational + ! inertia might be acceptable in many cases. + ! + ! Note that the minus sign of the entries is correct here: + DO k=A_f % Rows(kf),A_f % Rows(kf+1)-1 + CALL AddToMatrixElement(A_sf,ks,A_f % Cols(k),-weight*val*A_f0(k)) + END DO END DO END DO - END DO + END IF ! This should sum up to unity! CALL AddToMatrixElement(A_f,kf,kf,weight) @@ -17818,7 +17943,12 @@ SUBROUTINE StructureCouplingAssembly(Solver, FVar, SVar, A_f, A_s, A_fs, A_sf, & END DO END DO DEALLOCATE( Basis, dBasisdx, Nodes % x, Nodes % y, Nodes % z ) - DEALLOCATE(A_f0, NodeHits, InterfacePerm) + DEALLOCATE(A_f0, NodeHits, InterfacePerm, InterfaceElems) + IF (DrillingDOFs) THEN + DEALLOCATE(rhs0) + IF (DoMass) DEALLOCATE(Mass0) + END IF + END BLOCK END IF diff --git a/fem/src/modules/BeamSolver3D.F90 b/fem/src/modules/BeamSolver3D.F90 index 5f01bdcd73..9dff25dc22 100644 --- a/fem/src/modules/BeamSolver3D.F90 +++ b/fem/src/modules/BeamSolver3D.F90 @@ -85,6 +85,7 @@ END SUBROUTINE TimoshenkoSolver_Init0 SUBROUTINE TimoshenkoSolver(Model, Solver, dt, TransientSimulation) !------------------------------------------------------------------------------ USE DefUtils + USE SolidMechanicsUtils IMPLICIT NONE !------------------------------------------------------------------------------ @@ -126,7 +127,8 @@ SUBROUTINE TimoshenkoSolver(Model, Solver, dt, TransientSimulation) nd = GetElementNOFDOFs() nb = GetElementNOFBDOFs() - CALL LocalMatrix(Element, n, nd+nb, nb, TransientSimulation) + CALL BeamStiffnessMatrix(Element, n, nd+nb, nb, TransientSimulation, & + MassAssembly=TransientSimulation) END DO CALL DefaultFinishBulkAssembly() @@ -144,338 +146,6 @@ SUBROUTINE TimoshenkoSolver(Model, Solver, dt, TransientSimulation) END DO CALL DefaultFinish() - -CONTAINS - -!------------------------------------------------------------------------------ -! Integrate and assemble the local stiffness matrix. The local DOFs always -! correspond to the displacement components along the tangent direction and the -! principal axes of the cross section. The transformation to global DOFs is done -! within this subroutine. The stiffness matrix K corresponding to the global -! DOFs is thus obtained as K = R^T k R and the RHS vector F is obtained as -! F = R^T f. -!------------------------------------------------------------------------------ - SUBROUTINE LocalMatrix(Element, n, nd, nb, TransientSimulation) -!------------------------------------------------------------------------------ - IMPLICIT NONE - TYPE(Element_t), POINTER, INTENT(IN) :: Element - INTEGER, INTENT(IN) :: n, nd, nb - LOGICAL, INTENT(IN) :: TransientSimulation -!------------------------------------------------------------------------------ - TYPE(ValueList_t), POINTER :: BodyForce, Material - TYPE(Nodes_t) :: Nodes, LocalNodes - TYPE(GaussIntegrationPoints_t) :: IP - - LOGICAL :: Found, Stat - - INTEGER :: DOFs - INTEGER :: i, t, p, q - INTEGER :: i0, p0, q0 - - REAL(KIND=dp), POINTER :: ArrayPtr(:,:) => NULL() - REAL(KIND=dp), POINTER :: StiffBlock(:,:), MassBlock(:,:) - REAL(KIND=dp), DIMENSION(3), PARAMETER :: ZBasis = (/ 0.0d0, 0.0d0, 0.1d1 /) - - REAL(KIND=dp), TARGET :: Mass(6*nd,6*nd), Stiff(6*nd,6*nd), Damp(6*nd,6*nd) - REAL(KIND=dp) :: Force(6*nd) - REAL(KIND=dp) :: RBlock(3,3), R(6*nd,6*nd) - REAL(KIND=dp) :: Basis(nd), dBasis(nd,3), DetJ, Weight - REAL(KIND=dp) :: Youngs_Modulus(n), Shear_Modulus(n), Area(n), Density(n) - REAL(KIND=dp) :: Torsional_Constant(n) - REAL(KIND=dp) :: Area_Moment_2(n), Area_Moment_3(n) - REAL(KIND=dp) :: Mass_Inertia_Moment(n) - REAL(KIND=dp) :: Load(3,n), f(3) - REAL(KIND=dp) :: E, A, G, rho - REAL(KIND=dp) :: EA, GA, MOI, Mass_per_Length - REAL(KIND=dp) :: E_diag(3) - - REAL(KIND=dp) :: p1(3), p2(3), e1(3), e2(3), e3(3) - REAL(KIND=dp) :: L, Norm - - SAVE Nodes, LocalNodes -!------------------------------------------------------------------------------ - IF (n > 2) CALL Fatal('BeamSolver3D', & - 'Only 2-node background meshes supported currently') - - DOFs = 6 -! dim = CoordinateSystemDimension() - - CALL GetElementNodes(Nodes) - - Mass = 0.0_dp - Stiff = 0.0_dp - Damp = 0.0_dp - Force = 0.0_dp - - BodyForce => GetBodyForce() - IF ( ASSOCIATED(BodyForce) ) THEN - ! - ! Force components refer to the basis of the global frame: - ! - Load(1,1:n) = GetReal(BodyForce, 'Body Force 1', Found) - Load(2,1:n) = GetReal(BodyForce, 'Body Force 2', Found) - Load(3,1:n) = GetReal(BodyForce, 'Body Force 3', Found) - ELSE - Load = 0.0_dp - END IF - - Material => GetMaterial() - Youngs_Modulus(1:n) = GetReal(Material, 'Youngs Modulus', Found) - Shear_Modulus(1:n) = GetReal(Material, 'Shear Modulus', Found) - Area(1:n) = GetReal(Material, 'Cross Section Area', Found) - Torsional_Constant(1:n) = GetReal(Material, 'Torsional Constant', Found) - Area_Moment_2(1:n) = GetReal(Material, 'Second Moment of Area 2', Found) - Area_Moment_3(1:n) = GetReal(Material, 'Second Moment of Area 3', Found) - - IF (TransientSimulation) THEN - Density(1:n) = GetReal(Material, 'Density', Found) - END IF - - ! - ! Compute the tangent vector e1 to the beam axis: - ! - p1(1) = Nodes % x(1) - p1(2) = Nodes % y(1) - p1(3) = Nodes % z(1) - p2(1) = Nodes % x(2) - p2(2) = Nodes % y(2) - p2(3) = Nodes % z(2) - e1 = p2 - p1 - L = SQRT(SUM(e1(:)**2)) - e1 = 1.0_dp/L * e1 - ! - ! Cross section parameters are given with respect to a local frame. - ! Determine its orientation: - ! - ArrayPtr => ListGetConstRealArray(Material, 'Principal Direction 2', Found) - IF (Found) THEN - e2 = 0.0d0 - DO i=1,SIZE(ArrayPtr,1) - e2(i) = ArrayPtr(i,1) - END DO - Norm = SQRT(SUM(e2(:)**2)) - e2 = 1.0_dp/Norm * e2 - ELSE - e2 = -ZBasis - END IF - IF (ABS(DOT_PRODUCT(e1,e2)) > 100.0_dp * AEPS) CALL Fatal('BeamSolver3D', & - 'Principal Direction 2 should be orthogonal to the beam axis') - e3 = CrossProduct(e1, e2) - - ! - ! Build the transformation matrix in order to switch to the global DOFs - ! - R = 0.0d0 - RBlock(1,1:3) = e1(1:3) - RBlock(2,1:3) = e2(1:3) - RBlock(3,1:3) = e3(1:3) - DO i=1,nd-nb - i0 = (i-1)*DOFs - R(i0+1:i0+3,i0+1:i0+3) = RBlock(1:3,1:3) - R(i0+4:i0+6,i0+4:i0+6) = RBlock(1:3,1:3) - END DO - - ! - ! Allocate an additional variable so as to write nodes data with respect to - ! the local frame. - ! - IF (.NOT. ASSOCIATED(LocalNodes % x)) THEN - ALLOCATE(LocalNodes % x(n), LocalNodes % y(n), LocalNodes % z(n) ) - LocalNodes % NumberOfNodes = n - LocalNodes % y(:) = 0.0_dp - LocalNodes % z(:) = 0.0_dp - END IF - LocalNodes % x(1) = 0.0d0 - LocalNodes % x(2) = L - - !----------------------- - ! Numerical integration: - !----------------------- - IP = GaussPoints( Element ) - DO t=1,IP % n - !-------------------------------------------------------------- - ! Basis function values & derivatives at the integration point: - !-------------------------------------------------------------- - stat = ElementInfo(Element, LocalNodes, IP % U(t), IP % V(t), & - IP % W(t), detJ, Basis, dBasis) - - !------------------------------------------ - ! The model data at the integration point: - !------------------------------------------ - f(1) = SUM(Basis(1:n) * Load(1,1:n)) - f(2) = SUM(Basis(1:n) * Load(2,1:n)) - f(3) = SUM(Basis(1:n) * Load(3,1:n)) - - ! TO DO: Add option to give the applied moment load - - E = SUM(Basis(1:n) * Youngs_Modulus(1:n)) - G = SUM(Basis(1:n) * Shear_Modulus(1:n)) - A = SUM(Basis(1:n) * Area(1:n)) - - E_diag(1) = G * SUM(Basis(1:n) * Torsional_Constant(1:n)) - E_diag(2) = E * SUM(Basis(1:n) * Area_Moment_2(1:n)) - E_diag(3) = E * SUM(Basis(1:n) * Area_Moment_3(1:n)) - - IF (TransientSimulation) THEN - rho = SUM(Basis(1:n) * Density(1:n)) - MOI = rho/E * sqrt(E_diag(2)**2 + E_diag(3)**2) - Mass_per_Length = rho * A - END IF - - GA = G*A - EA = E*A - - ! TO DO: Add option to give shear correction factors - - Weight = IP % s(t) * DetJ - - DO p=1,nd - p0 = (p-1)*DOFs - DO q=1,nd - q0 = (q-1)*DOFs - StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) - MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) - ! - ! (Du',v'): - ! - StiffBlock(1,1) = StiffBlock(1,1) + & - EA * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(2,2) = StiffBlock(2,2) + & - GA * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(3,3) = StiffBlock(3,3) + & - GA * dBasis(q,1) * dBasis(p,1) * Weight - - IF (TransientSimulation) THEN - MassBlock(1,1) = MassBlock(1,1) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - MassBlock(2,2) = MassBlock(2,2) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - MassBlock(3,3) = MassBlock(3,3) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - END IF - - IF (q > n) CYCLE - ! - ! -(D theta x t,v'): - ! - StiffBlock(2,6) = StiffBlock(2,6) - & - GA * Basis(q) * dBasis(p,1) * Weight - StiffBlock(3,5) = StiffBlock(3,5) + & - GA * Basis(q) * dBasis(p,1) * Weight - END DO - - Force(p0+1) = Force(p0+1) + Weight * DOT_PRODUCT(f,e1)* Basis(p) - Force(p0+2) = Force(p0+2) + Weight * DOT_PRODUCT(f,e2)* Basis(p) - Force(p0+3) = Force(p0+3) + Weight * DOT_PRODUCT(f,e3)* Basis(p) - - IF (p > n) CYCLE - - DO q=1,nd - q0 = (q-1)*DOFs - StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) - MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) - ! - ! -(D u',psi x t): - ! - StiffBlock(5,3) = StiffBlock(5,3) + & - GA * Basis(p) * dBasis(q,1) * Weight - StiffBlock(6,2) = StiffBlock(6,2) - & - GA * Basis(p) * dBasis(q,1) * Weight - - IF (q > n) CYCLE - - ! - ! (E theta',psi') + (D theta x t,psi x t): - ! - StiffBlock(4,4) = StiffBlock(4,4) + & - E_diag(1) * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(5,5) = StiffBlock(5,5) + & - E_diag(2) * dBasis(q,1) * dBasis(p,1) * Weight + & - GA * Basis(p) * Basis(q) * Weight - StiffBlock(6,6) = StiffBlock(6,6) + & - E_diag(3) * dBasis(q,1) * dBasis(p,1) * Weight + & - GA * Basis(p) * Basis(q) * Weight - - IF (TransientSimulation) THEN - MassBlock(4,4) = MassBlock(4,4) + MOI * Basis(q) * Basis(p) * Weight - MassBlock(5,5) = MassBlock(5,5) + rho/E * E_diag(2) * & - Basis(q) * Basis(p) * Weight - MassBlock(6,6) = MassBlock(6,6) + rho/E * E_diag(3) * & - Basis(q) * Basis(p) * Weight - END IF - - END DO - END DO - END DO - - CALL BeamCondensate(nd-nb, nb, DOFs, 3, Stiff, Force) - - !------------------------------------------------------- - ! Transform to the global DOFs: - !------------------------------------------------------- - DOFs = (nd-nb)*DOFs - Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Stiff(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - Force(1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)),Force(1:DOFs)) - - IF (TransientSimulation) THEN - Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Mass(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - CALL Default2ndOrderTime(Mass, Damp, Stiff, Force) - END IF - - CALL DefaultUpdateEquations(Stiff, Force) -!------------------------------------------------------------------------------ - END SUBROUTINE LocalMatrix -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ -SUBROUTINE BeamCondensate(n, nb, dofs, dim, K, F, F1 ) -!------------------------------------------------------------------------------ - USE LinearAlgebra - IMPLICIT NONE - INTEGER, INTENT(IN) :: n ! Nodes after condensation - INTEGER, INTENT(IN) :: nb ! The number of bubble basis functions - INTEGER, INTENT(IN) :: dofs ! DOFs per node - INTEGER, INTENT(IN) :: dim ! The first dim fields have bubbles - REAL(KIND=dp), INTENT(INOUT) :: K(:,:) ! The stiffness matrix - REAL(KIND=dp), INTENT(INOUT) :: F(:) ! The RHS vector - REAL(KIND=dp), OPTIONAL, INTENT(INOUT) :: F1(:) ! Some other RHS vector -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Kbl(nb*dim,n*dofs), Kbb(nb*dim,nb*dim), Fb(nb*dim) - REAL(KIND=dp) :: Klb(n*dofs,nb*dim) - - INTEGER :: i, m, p, Cdofs(dofs*n), Bdofs(dim*nb) -!------------------------------------------------------------------------------ - - Cdofs(1:n*dofs) = (/ (i, i=1,n*dofs) /) - - m = 0 - DO p = 1,nb - DO i = 1,dim - m = m + 1 - Bdofs(m) = dofs*(n+p-1) + i - END DO - END DO - - Kbb = K(Bdofs,Bdofs) - Kbl = K(Bdofs,Cdofs) - Klb = K(Cdofs,Bdofs) - Fb = F(Bdofs) - - CALL InvertMatrix( Kbb,nb*dim ) - - F(1:dofs*n) = F(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) - K(1:dofs*n,1:dofs*n) = & - K(1:dofs*n,1:dofs*n) - MATMUL( Klb, MATMUL( Kbb,Kbl ) ) - - IF (PRESENT(F1)) THEN - Fb = F1(Bdofs) - F1(1:dofs*n) = F1(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) - END IF -!------------------------------------------------------------------------------ - END SUBROUTINE BeamCondensate -!------------------------------------------------------------------------------ !------------------------------------------------------------------------------ END SUBROUTINE TimoshenkoSolver diff --git a/fem/src/modules/CircuitsAndDynamics.F90 b/fem/src/modules/CircuitsAndDynamics.F90 index 65e6bb4177..04012121ff 100644 --- a/fem/src/modules/CircuitsAndDynamics.F90 +++ b/fem/src/modules/CircuitsAndDynamics.F90 @@ -1802,7 +1802,7 @@ SUBROUTINE CircuitsOutput(Model,Solver,dt,Transient) !------------------------------------------------------------------------------ LOGICAL, SAVE :: EEC, First =.TRUE. LOGICAL :: EEC_lim - REAL, SAVE :: EEC_freq, EEC_time_0 + REAL(KIND=dp), SAVE :: EEC_freq, EEC_time_0 INTEGER, SAVE :: EEC_max, EEC_cnt = 0 REAL :: TTime TYPE(ValueList_t), POINTER :: SolverParams diff --git a/fem/src/modules/ElectricForce.F90 b/fem/src/modules/ElectricForce.F90 index e4aafe339d..325f37aa74 100644 --- a/fem/src/modules/ElectricForce.F90 +++ b/fem/src/modules/ElectricForce.F90 @@ -334,7 +334,7 @@ SUBROUTINE StatElecForce( Model,Solver,dt,TransientSimulation ) DEALLOCATE( LocalPotential, Permittivity ) DEALLOCATE( NodalWeight ) - ! These are some obsolite stuff but let's do this anyways + ! These are some obsolete stuff but let's do this anyways IF( CalculateField ) THEN FieldVar % PrimaryMesh => Mesh CALL InvalidateVariable( Model % Meshes, Mesh, 'Electric Force Density' ) diff --git a/fem/src/modules/FacetShellSolve.F90 b/fem/src/modules/FacetShellSolve.F90 index 8fb0b41c2a..411ae9772d 100644 --- a/fem/src/modules/FacetShellSolve.F90 +++ b/fem/src/modules/FacetShellSolve.F90 @@ -905,6 +905,9 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & ElementNumber, NodalPoisson, NodalYoung, NodalDampingCoef, & LocalDeflection, LargeDeflection, StabilityAnalysis, Nvector ) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: StrainEnergyDensity, ShearCorrectionFactor, & + IsotropicElasticity + REAL(KIND=dp) :: STIFF(:,:), DAMP(:,:), MASS(:,:), & Amatrix(3,3), Bmatrix(3,3), Dmatrix(3,3), Astarmatrix(2,2) REAL(KIND=dp) :: FORCE(:) @@ -1184,7 +1187,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & END IF END DO - CALL AddEnergy(STIFF, Dmatrix, Kappa, 3, 6*n, s) + CALL StrainEnergyDensity(STIFF, Dmatrix, Kappa, 3, 6*n, s) ! In-plane stiffness: ! ------------------- @@ -1206,7 +1209,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & END IF END DO - CALL AddEnergy(STIFF, Amatrix, EPS, 3, 6*n, s) + CALL StrainEnergyDensity(STIFF, Amatrix, EPS, 3, 6*n, s) ! Coupling through the B-matrix: ! ------------------------------ @@ -1249,7 +1252,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & Gammaa(1:2,6*p-3) = dBasisdx(p,1:2) END DO - CALL AddEnergy(STIFF, Astarmatrix, Gammaa, 2, 6*n, SCF*s) + CALL StrainEnergyDensity(STIFF, Astarmatrix, Gammaa, 2, 6*n, SCF*s) ! Drilling DOFs (in-plane rotations): ! ----------------------------------- @@ -1260,7 +1263,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & Omega(1,6*p-0) = Basis(p) ! rotation END DO - CALL AddEnergy(STIFF, Gdrilling, Omega, 1, 6*n, s) + CALL StrainEnergyDensity(STIFF, Gdrilling, Omega, 1, 6*n, s) ! Newton lin. terms: ! ----------------- @@ -1528,7 +1531,9 @@ SUBROUTINE LocalStress( Element, n, Nodes, StabParam1, StabParam2, & Mten, MtenMaterial, NodalYoung, NodalPoisson, NodalThickness, & LargeDeflection ) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: ShearCorrectionFactor, IsotropicElasticity IMPLICIT NONE + REAL(KIND=dp) :: StabParam1, StabParam2, LocalDeflection(:), & Weight3(:), Weight4(:), Eps(3,3), Kap(3,3), NTen(3,3), MTen(3,3), & NtenMaterial(2,2), MtenMaterial(2,2), & @@ -1953,6 +1958,7 @@ END SUBROUTINE SwitchToLocal !------------------------------------------------------------------------------ FUNCTION LocalBasis( Nodes, n ) RESULT( BasisVectors ) !------------------------------------------------------------------------------ + USE ElementDescription, ONLY: CrossProduct TYPE(Nodes_t) :: Nodes REAL(KIND=dp) :: BasisVectors(3,3) INTEGER :: n @@ -1977,43 +1983,11 @@ FUNCTION LocalBasis( Nodes, n ) RESULT( BasisVectors ) BasisVectors(1:3,1) = Tangent1 BasisVectors(1:3,2) = Tangent2 - SUM( Tangent1 * Tangent2 ) * Tangent1 BasisVectors(1:3,2) = BasisVectors(1:3,2) / SQRT( SUM( BasisVectors(1:3,2)**2 ) ) - BasisVectors(1:3,3) = CrossProductL( BasisVectors(1:3,1), BasisVectors(1:3,2) ) + BasisVectors(1:3,3) = CrossProduct( BasisVectors(1:3,1), BasisVectors(1:3,2) ) !------------------------------------------------------------------------------ END FUNCTION LocalBasis !------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ - SUBROUTINE IsotropicElasticity(Ematrix, & - Gmatrix,Poisson,Young,Thickness,Basis,n) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Ematrix(:,:), Gmatrix(:,:), Basis(:) - REAL(KIND=dp) :: Poisson(:), Young(:), Thickness(:) - REAL(KIND=dp) :: Euvw, Puvw, Guvw, Tuvw - INTEGER :: n -!------------------------------------------------------------------------------ - Euvw = SUM( Young(1:n) * Basis(1:n) ) - Puvw = SUM( Poisson(1:n) * Basis(1:n) ) - Tuvw = SUM( Thickness(1:n)* Basis(1:n) ) - Guvw = Euvw/(2.0d0*(1.0d0 + Puvw)) - - Ematrix = 0.0d0 - Ematrix(1,1) = 1.0d0 - Ematrix(1,2) = Puvw - Ematrix(2,1) = Puvw - Ematrix(2,2) = 1.0d0 - Ematrix(3,3) = (1.0d0-Puvw)/2.0d0 - Ematrix = Ematrix * Euvw * (Tuvw**3) / (12.0d0 * (1.0d0 - Puvw*Puvw)) - - Gmatrix = 0.0d0 - Gmatrix(1,1) = Guvw*Tuvw - Gmatrix(2,2) = Guvw*Tuvw -!------------------------------------------------------------------------------ - END SUBROUTINE IsotropicElasticity -!------------------------------------------------------------------------------ - -!============================================================================== - !------------------------------------------------------------------------------ SUBROUTINE IsotropicInPlaneElasticity( Ematrix, & Poisson, Young, Thickness, Basis, n ) @@ -2039,89 +2013,6 @@ SUBROUTINE IsotropicInPlaneElasticity( Ematrix, & END SUBROUTINE IsotropicInPlaneElasticity !------------------------------------------------------------------------------ -!============================================================================== - -!------------------------------------------------------------------------------ - SUBROUTINE ShearCorrectionFactor(Kappa,Thickness,x,y,n,StabParam) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Kappa,Thickness,x(:),y(:),StabParam - INTEGER :: n -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: x21,x32,x43,x13,x14,y21,y32,y43,y13,y14, & - l21,l32,l43,l13,l14,alpha,h -!------------------------------------------------------------------------------ - Kappa = 1.0d0 - SELECT CASE(n) - CASE(3) - alpha = 0.20d0 * StabParam - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x13 = x(1)-x(1) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y13 = y(1)-y(1) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l13 = SQRT(x13**2 + y13**2) - h = MAX(l21,l32,l13) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - CASE(4) - alpha = 0.10d0 * StabParam - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x43 = x(4)-x(3) - x14 = x(1)-x(4) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y43 = y(4)-y(3) - y14 = y(1)-y(4) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l43 = SQRT(x43**2 + y43**2) - l14 = SQRT(x14**2 + y14**2) - h = MAX(l21,l32,l43,l14) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - CASE DEFAULT - CALL Fatal('ShellSolver','Illegal number of nodes for Smitc elements') - END SELECT -!------------------------------------------------------------------------------ - END SUBROUTINE ShearCorrectionFactor -!------------------------------------------------------------------------------ - -!============================================================================== - -!------------------------------------------------------------------------------ - SUBROUTINE AddEnergy(A,B,C,m,n,s) -!------------------------------------------------------------------------------ -! Performs the operation -! -! A = A + C' * B * C * s -! -! with -! -! Size( A ) = n x n -! Size( B ) = m x m -! Size( C ) = m x n -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: A(:,:),B(:,:),C(:,:),s - INTEGER :: m,n -!------------------------------------------------------------------------------ - INTEGER :: i,j,k,l -!------------------------------------------------------------------------------ - DO i=1,n - DO j=1,n - DO k=1,m - DO l=1,m - A(i,j) = A(i,j) + C(k,i)*B(k,l)*C(l,j) * s - END DO - END DO - END DO - END DO -!------------------------------------------------------------------------------ - END SUBROUTINE AddEnergy -!------------------------------------------------------------------------------ - -!============================================================================== !------------------------------------------------------------------------------ SUBROUTINE AddInnerProducts(A,B,C,D,m,n,s) @@ -2160,6 +2051,7 @@ END SUBROUTINE AddInnerProducts !------------------------------------------------------------------------------ SUBROUTINE CovariantInterpolation(ShearStrain,Basis,X,Y,U,V,n) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: Jacobi3, Jacobi4 REAL(KIND=dp) :: ShearStrain(:,:),Basis(:),X(:),Y(:),U,V INTEGER :: n !------------------------------------------------------------------------------ @@ -2313,77 +2205,6 @@ SUBROUTINE CovariantInterpolation(ShearStrain,Basis,X,Y,U,V,n) END SUBROUTINE CovariantInterpolation !------------------------------------------------------------------------------ -!============================================================================== - -!------------------------------------------------------------------------------ - SUBROUTINE Jacobi3(Jmat,invJ,detJ,x,y) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Jmat(:,:),invJ(:,:),detJ,x(:),y(:) -!------------------------------------------------------------------------------ - Jmat(1,1) = x(2)-x(1) - Jmat(2,1) = x(3)-x(1) - Jmat(1,2) = y(2)-y(1) - Jmat(2,2) = y(3)-y(1) - - detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) - - invJ(1,1) = Jmat(2,2)/detJ - invJ(2,2) = Jmat(1,1)/detJ - invJ(1,2) = -Jmat(1,2)/detJ - invJ(2,1) = -Jmat(2,1)/detJ -!------------------------------------------------------------------------------ - END SUBROUTINE Jacobi3 -!------------------------------------------------------------------------------ - -!============================================================================== - -!------------------------------------------------------------------------------ - SUBROUTINE Jacobi4(Jmat,invJ,detJ,xi,eta,x,y) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Jmat(:,:),invJ(:,:),detJ,xi,eta,x(:),y(:) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: dNdxi(4), dNdeta(4) - INTEGER :: i - - dNdxi(1) = -(1-eta)/4.0d0 - dNdxi(2) = (1-eta)/4.0d0 - dNdxi(3) = (1+eta)/4.0d0 - dNdxi(4) = -(1+eta)/4.0d0 - dNdeta(1) = -(1-xi)/4.0d0 - dNdeta(2) = -(1+xi)/4.0d0 - dNdeta(3) = (1+xi)/4.0d0 - dNdeta(4) = (1-xi)/4.0d0 - - Jmat = 0.0d0 - DO i=1,4 - Jmat(1,1) = Jmat(1,1) + dNdxi(i)*x(i) - Jmat(1,2) = Jmat(1,2) + dNdxi(i)*y(i) - Jmat(2,1) = Jmat(2,1) + dNdeta(i)*x(i) - Jmat(2,2) = Jmat(2,2) + dNdeta(i)*y(i) - END DO - - detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) - - invJ(1,1) = Jmat(2,2)/detJ - invJ(2,2) = Jmat(1,1)/detJ - invJ(1,2) = -Jmat(1,2)/detJ - invJ(2,1) = -Jmat(2,1)/detJ -!------------------------------------------------------------------------------ - END SUBROUTINE Jacobi4 -!------------------------------------------------------------------------------ - -!============================================================================== - -!------------------------------------------------------------------------------ - FUNCTION CrossProductL( v1, v2 ) RESULT( v3 ) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: v1(3), v2(3), v3(3) - v3(1) = v1(2)*v2(3) - v1(3)*v2(2) - v3(2) = -v1(1)*v2(3) + v1(3)*v2(1) - v3(3) = v1(1)*v2(2) - v1(2)*v2(1) -!------------------------------------------------------------------------------ - END FUNCTION CrossProductL -!------------------------------------------------------------------------------ !------------------------------------------------------------------------------ END SUBROUTINE ShellSolver diff --git a/fem/src/modules/HeatSolveVec.F90 b/fem/src/modules/HeatSolveVec.F90 index 5af40302e0..b6b79d536a 100644 --- a/fem/src/modules/HeatSolveVec.F90 +++ b/fem/src/modules/HeatSolveVec.F90 @@ -1001,12 +1001,17 @@ SUBROUTINE LocalMatrixDiffuseGray( Element, n, nd, nb ) n = GetElementNOFNodes(Element) CALL GetElementNodes( Nodes, UElement=Element) n = Element % TYPE % NumberOfNodes - + + IF( .NOT. ASSOCIATED( Element % BoundaryInfo % GebhardtFactors ) ) THEN + CALL Fatal('HeatSolverVec','Gebhardt factors not calculated for boundary!') + END IF + Fact => Element % BoundaryInfo % GebhardtFactors % Factors ElementList => Element % BoundaryInfo % GebhardtFactors % Elements bindex = Element % ElementIndex - Solver % Mesh % NumberOfBulkElements nf = Element % BoundaryInfo % GebhardtFactors % NumberOfFactors + nf_imp = Element % BoundaryInfo % GebhardtFactors % NumberOfImplicitFactors IF( nf_imp == 0 ) nf_imp = nf diff --git a/fem/src/modules/MagnetoDynamics/CalcFields.F90 b/fem/src/modules/MagnetoDynamics/CalcFields.F90 index 8dfed31d51..90b927505c 100644 --- a/fem/src/modules/MagnetoDynamics/CalcFields.F90 +++ b/fem/src/modules/MagnetoDynamics/CalcFields.F90 @@ -49,21 +49,22 @@ SUBROUTINE MagnetoDynamicsCalcFields_Init0(Model,Solver,dt,Transient) LOGICAL :: Transient !------------------------------------------------------------------------------ CHARACTER(LEN=MAX_NAME_LEN) :: sname,pname - LOGICAL :: Found, ElementalFields, RealField, FoundVar + LOGICAL :: Found, ElementalFields, RealField, FoundVar, Hcurl INTEGER, POINTER :: Active(:) - INTEGER :: mysolver,i,j,k,l,n,m,vDOFs, soln + INTEGER :: mysolver,i,j,k,l,n,m,vDOFs, soln,pIndex TYPE(ValueList_t), POINTER :: SolverParams, DGSolverParams TYPE(Solver_t), POINTER :: Solvers(:), PSolver ! This is really using DG so we don't need to make any dirty tricks to create DG fields ! as is done in this initialization. SolverParams => GetSolverParams() - + ! The only purpose of this parsing of the variable name is to identify ! whether the field is real or complex. As the variable has not been ! created at this stage we have to do some dirty parsing. pname = GetString(SolverParams, 'Potential variable', Found) vdofs = 0 + pIndex = 0 FoundVar = .FALSE. IF( Found ) THEN @@ -81,44 +82,69 @@ SUBROUTINE MagnetoDynamicsCalcFields_Init0(Model,Solver,dt,Transient) k = k+j IF(k 0 ) THEN - CALL Info('MagnetoDynamicsCalcFields_Init0','The target solver seems to be real valued',Level=12) - Vdofs = 1 + Hcurl = .TRUE. + vDofs = 2 EXIT END IF - j = INDEX( sname,'WhitneyAVHarmonicSolver') + j = INDEX( sname,'MagnetoDynamics2DHarmonic') IF( j > 0 ) THEN - CALL Info('MagnetoDynamicsCalcFields_Init0','The target solver seems to be complex valued',Level=12) Vdofs = 2 EXIT END IF + + j = INDEX( sname,'WhitneyAVSolver') + IF( j > 0 ) THEN + Hcurl = .TRUE. + vDofs = 1 + EXIT + END IF + + j = INDEX( sname,'MagnetoDynamics2D') + IF( j > 0 ) THEN + Vdofs = 1 + EXIT + END IF END DO IF( Vdofs == 0 ) THEN CALL Fatal('MagnetoDynamicsCalcFields_Init0','Could not determine target variable type (real or complex)') END IF + pIndex = i END IF - IF ( Vdofs==0 ) Vdofs=1 + RealField = ( Vdofs /= 2 ) + IF( RealField ) THEN + CALL Info('MagnetoDynamicsCalcFields_Init0','The target solver seems to be real valued',Level=12) + ELSE + CALL Info('MagnetoDynamicsCalcFields_Init0','The target solver seems to be complex valued',Level=12) + END IF - RealField = ( Vdofs == 1 ) - CALL ListAddLogical( SolverParams, 'Target Variable Real Field', RealField ) + CALL ListAddNewLogical( SolverParams, 'Target Variable Real Field', RealField ) + CALL Info('MagnetoDynamicsCalcFields_Init0','Target Variable Solver Index: '& + //TRIM(I2S(pIndex)),Level=12) + CALL ListAddNewInteger( SolverParams, 'Target Variable Solver Index', pIndex ) !------------------------------------------------------------------------------ END SUBROUTINE MagnetoDynamicsCalcFields_Init0 @@ -163,8 +189,14 @@ SUBROUTINE MagnetoDynamicsCalcFields_Init(Model,Solver,dt,Transient) ! if executed before the actual computations... ! ----------------------------------------------------------------------- CALL ListAddConstReal(Model % Simulation,'res: Eddy current power',0._dp) - CALL ListAddConstReal(Model % Simulation,'res: Magnetic Field Energy',0._dp) + IF( ListGetLogical( SolverParams,'Separate Magnetic Energy',Found ) ) THEN + CALL ListAddConstReal(Model % Simulation,'res: Electric Field Energy',0._dp) + CALL ListAddConstReal(Model % Simulation,'res: Magnetic Field Energy',0._dp) + ELSE + CALL ListAddConstReal(Model % Simulation,'res: ElectroMagnetic Field Energy',0._dp) + END IF + IF (GetLogical(SolverParams,'Show Angular Frequency',Found)) & CALL ListAddConstReal(Model % Simulation,'res: Angular Frequency',0._dp) @@ -486,24 +518,29 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) ! a background element of type 827): !------------------------------------------------------------------------------ REAL(KIND=dp) :: WBasis(54,3), RotWBasis(54,3), Basis(27), dBasisdx(27,3) - REAL(KIND=dp) :: SOL(2,81), PSOL(81), ElPotSol(1,27), R(27), C(27) + REAL(KIND=dp) :: SOL(2,81), PSOL(81), ElPotSol(1,27), C(27) REAL(KIND=dp) :: Wbase(27), alpha(27), NF_ip(27,3) REAL(KIND=dp) :: PR(27), omega_velo(3,27), lorentz_velo(3,27) - COMPLEX(KIND=dp) :: Magnetization(3,27), BodyForceCurrDens(3,27) + COMPLEX(KIND=dp) :: Magnetization(3,27), BodyForceCurrDens(3,27) + COMPLEX(KIND=dp) :: R_Z(27) !------------------------------------------------------------------------------ REAL(KIND=dp) :: s,u,v,w, Norm - REAL(KIND=dp) :: B(2,3), E(2,3), JatIP(2,3), VP_ip(2,3), JXBatIP(2,3), CC_J(2,3), B2 - REAL(KIND=dp) :: detJ, C_ip, R_ip, PR_ip, ST(3,3), Omega, ThinLinePower, Power, Energy, w_dens, R_t_ip(3,3) + REAL(KIND=dp) :: B(2,3), E(2,3), JatIP(2,3), VP_ip(2,3), JXBatIP(2,3), CC_J(2,3), HdotB + REAL(KIND=dp) :: detJ, C_ip, PR_ip, ST(3,3), Omega, ThinLinePower, Power, Energy(3), w_dens REAL(KIND=dp) :: Freq, FreqPower, FieldPower, LossCoeff, ValAtIP REAL(KIND=dp) :: Freq2, FreqPower2, FieldPower2, LossCoeff2 REAL(KIND=dp) :: ComponentLoss(2,2), rot_velo(3), angular_velo(3) REAL(KIND=dp) :: Coeff, Coeff2, TotalLoss(3), LumpedForce(3), localAlpha, localV(2), nofturns, coilthickness REAL(KIND=dp) :: Flux(2), AverageFluxDensity(2), Area, N_j, wvec(3), PosCoord(3), TorqueDeprecated(3) + REAL(KIND=dp) :: R_ip, mu_r COMPLEX(KIND=dp) :: MG_ip(3), BodyForceCurrDens_ip(3) COMPLEX(KIND=dp) :: CST(3,3) COMPLEX(KIND=dp) :: CMat_ip(3,3) COMPLEX(KIND=dp) :: imag_value, Zs + COMPLEX(KIND=dp), ALLOCATABLE :: Tcoef(:,:,:) + COMPLEX(KIND=dp), POINTER, SAVE :: Reluct_Z(:,:,:) => NULL() + COMPLEX(KIND=dp) :: R_ip_Z, Nu(3,3) INTEGER, PARAMETER :: ind1(6) = [1,2,3,1,2,1] INTEGER, PARAMETER :: ind2(6) = [1,2,3,2,3,3] @@ -521,11 +558,14 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) CHARACTER(LEN=MAX_NAME_LEN) :: Pname, CoilType, ElectricPotName, LossFile, CurrPathPotName TYPE(ValueList_t), POINTER :: Material, BC, BodyForce, BodyParams, SolverParams + LOGICAL :: Found, FoundMagnetization, stat, Cubic, LossEstimation, & CalcFluxLogical, CoilBody, PreComputedElectricPot, ImposeCircuitCurrent, & ItoJCoeffFound, ImposeBodyForceCurrent, HasVelocity, HasAngularVelocity, & HasLorenzVelocity, HaveAirGap, UseElementalNF, HasTensorReluctivity, & ImposeBodyForcePotential, JouleHeatingFromCurrent, HasZirka + LOGICAL :: PiolaVersion, ElementalFields, NodalFields, RealField, SecondOrder + LOGICAL :: CSymmetry, HBCurve, LorentzConductivity, HasThinLines=.FALSE. TYPE(GaussIntegrationPoints_t) :: IP TYPE(Nodes_t), SAVE :: Nodes @@ -545,20 +585,19 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) REAL(KIND=dp), ALLOCATABLE :: ThinLineCrossect(:),ThinLineCond(:) REAL(KIND=DP), POINTER :: Cwrk(:,:,:)=>NULL(), Cwrk_im(:,:,:)=>NULL() - COMPLEX(KIND=dp), ALLOCATABLE :: Tcoef(:,:,:) - REAL(KIND=dp), POINTER :: R_t(:,:,:) - LOGICAL :: PiolaVersion, ElementalFields, NodalFields, RealField, SecondOrder REAL(KIND=dp) :: ItoJCoeff, CircuitCurrent, CircEqVoltageFactor TYPE(ValueList_t), POINTER :: CompParams REAL(KIND=dp) :: DetF, F(3,3), G(3,3), GT(3,3) REAL(KIND=dp), ALLOCATABLE :: EBasis(:,:), CurlEBasis(:,:) - LOGICAL :: CSymmetry, HBCurve, LorentzConductivity, HasThinLines=.FALSE. + REAL(KIND=dp) :: xcoord, grads_coeff, val TYPE(ValueListEntry_t), POINTER :: HBLst - REAL(KIND=dp) :: HarmPowerCoeff = 0.5_dp + REAL(KIND=dp) :: HarmPowerCoeff REAL(KIND=dp) :: line_tangent(3) - INTEGER :: IOUnit + INTEGER :: IOUnit, pIndex + REAL(KIND=dp) :: SaveNorm + INTEGER :: NormIndex INTEGER, POINTER, SAVE :: SetPerm(:) => NULL() !------------------------------------------------------------------------------------------- @@ -570,23 +609,21 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) dim = CoordinateSystemDimension() SolverParams => GetSolverParams() - IF (GetLogical(SolverParams, 'Calculate harmonic peak power', Found)) HarmPowerCoeff = 1.0_dp + ! This is a hack to be able to control the norm that is tested for + NormIndex = GetInteger(SolverParams,'Show Norm Index',Found ) + SaveNorm = 0.0_dp + + IF (GetLogical(SolverParams, 'Calculate harmonic peak power', Found)) THEN + HarmPowerCoeff = 1.0_dp + ELSE + HarmPowerCoeff = 0.5_dp + END IF - Pname = GetString(SolverParams, 'Potential Variable',Found) - IF(.NOT. Found ) Pname = 'av' - Found = .FALSE. - DO i=1,Model % NumberOfSolvers - pSolver => Model % Solvers(i) - IF ( Pname == getVarName(pSolver % Variable)) THEN - Found = .TRUE. - EXIT - END IF - END DO + pIndex = ListGetInteger( SolverParams,'Target Variable Solver Index',UnfoundFatal=.TRUE.) + pSolver => Model % Solvers(pIndex) + pname = getVarName(pSolver % Variable) - IF(.NOT. Found ) THEN - CALL Fatal('MagnetoDynamicsCalcFields','Solver associated to potential variable > '& - //TRIM(Pname)//' < not found!') - END IF + CALL Info('MagnetoDynamicsCalcFields','Using potential variable: '//TRIM(Pname),Level=7) ! Inherit the solution basis from the primary solver vDOFs = pSolver % Variable % DOFs @@ -741,7 +778,7 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) IF ( ASSOCIATED(EL_ML2) ) ElementalFields=.TRUE. n = Mesh % MaxElementDOFs - ALLOCATE( MASS(n,n), FORCE(n,DOFs), Tcoef(3,3,n), RotM(3,3,n), Pivot(n), R_t(3,3,n)) + ALLOCATE( MASS(n,n), FORCE(n,DOFs), Tcoef(3,3,n), RotM(3,3,n), Pivot(n)) SOL = 0._dp; PSOL=0._dp @@ -780,7 +817,7 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) END IF - C = 0._dp; R=0._dp; PR=0._dp + C = 0._dp; PR=0._dp Magnetization = 0._dp Power = 0._dp; Energy = 0._dp @@ -944,7 +981,7 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) !--------------------------------------------------------------------------------------------- - + R_Z = CMPLX(0.0_dp, 0.0_dp, kind=dp) HasTensorReluctivity = .FALSE. CALL GetConstRealArray( Material, HB, 'H-B curve', Found ) IF ( ASSOCIATED(HB) ) THEN @@ -969,15 +1006,22 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) END IF END IF ELSE - CALL GetReluctivity(Material,R_t,n,HasTensorReluctivity) + ! + ! Seek reluctivity as complex-valued: A given reluctivity can be a tensor + ! + CALL GetReluctivity(Material,Reluct_Z,n,HasTensorReluctivity) IF (HasTensorReluctivity) THEN - IF (SIZE(R_t,1)==1 .AND. SIZE(R_t,2)==1) THEN - l = MIN(SIZE(R), SIZE(R_t,3)) - R(1:l) = R_t(1,1,1:l) + IF (SIZE(Reluct_Z,1)==1 .AND. SIZE(Reluct_Z,2)==1) THEN + l = MIN(SIZE(R_Z), SIZE(Reluct_Z,3)) + R_Z(1:l) = Reluct_Z(1,1,1:l) HasTensorReluctivity = .FALSE. + ELSE + R_Z = CMPLX(0.0_dp, 0.0_dp, kind=dp) END IF ELSE - CALL GetReluctivity(Material,R,n) + ! Seek via a given permeability: In this case the reluctivity will be + ! a complex scalar: + CALL GetReluctivity(Material,R_Z,n) END IF END IF @@ -1304,22 +1348,53 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) END SELECT END IF - + Nu = CMPLX(0.0d0, 0.0d0, kind=dp) IF ( ASSOCIATED(HB) ) THEN - Babs=SQRT(SUM(B(1,:)**2)) + IF (RealField) THEN + Babs=SQRT(SUM(B(1,:)**2)) + ELSE + Babs = SQRT(SUM(B(1,:)**2 + B(2,:)**2)) + END IF + Babs = MAX(Babs, 1.d-8) R_ip = InterpolateCurve(HBBval,HBHval,Babs,HBCval)/Babs w_dens = IntegrateCurve(HBBval,HBHval,HBCval,0._dp,Babs) + DO k=1,3 + Nu(k,k) = CMPLX(R_ip, 0.0d0, kind=dp) + END DO ELSE - R_ip = SUM( Basis(1:n)*R(1:n) ) - IF(HasTensorReluctivity) THEN - DO k = 1,3 - DO l = 1,3 - R_t_ip(k,l) = sum(Basis(1:n)*R_t(k,l,1:n)) + IF (HasTensorReluctivity) THEN + IF (SIZE(Reluct_Z,2) == 1) THEN + DO k = 1, MIN(3, SIZE(Reluct_Z,1)) + Nu(k,k) = SUM(Basis(1:n)*Reluct_Z(k,1,1:n)) END DO + ELSE + DO k = 1, MIN(3, SIZE(Reluct_Z,1)) + DO l = 1, MIN(3, SIZE(Reluct_Z,2)) + Nu(k,l) = sum(Basis(1:n)*Reluct_Z(k,l,1:n)) + END DO + END DO + END IF + R_ip = 0.0d0 + ELSE + R_ip_Z = SUM(Basis(1:n)*R_Z(1:n)) + DO k=1,3 + Nu(k,k) = R_ip_Z END DO - w_dens = 0.5*SUM(B(1,:)*MATMUL(R_t_ip,B(1,:))) + ! + ! The calculation of the Maxwell stress tensor doesn't yet support + ! a tensor-form reluctivity. Create the scalar reluctivity parameter + ! so that the Maxwell stress tensor may be calculated. The complex + ! part will be ignored. + ! + R_ip = REAL(R_ip_Z) + END IF + IF (RealField) THEN + w_dens = 0.5*SUM(B(1,:)*MATMUL(REAL(Nu), B(1,:))) + ELSE + ! This yields twice the time average: + w_dens = 0.5*( SUM(MATMUL(REAL(Nu), B(1,:)) * B(1,:)) + & + SUM(MATMUL(REAL(Nu), B(2,:)) * B(2,:)) ) END IF - w_dens = 0.5*R_ip*SUM(B(1,:)**2) END IF PR_ip = SUM( Basis(1:n)*PR(1:n) ) @@ -1341,35 +1416,37 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) END SELECT END DO END IF + + IF (RealField) THEN + HdotB = SUM(MATMUL(REAL(Nu), B(1,:)) * B(1,:)) + ELSE + HdotB = SUM(MATMUL(REAL(Nu), B(1,:)) * B(1,:)) + & + SUM(MATMUL(REAL(Nu), B(2,:)) * B(2,:)) + END IF IF (ASSOCIATED(NF).OR.ASSOCIATED(EL_NF)) THEN NF_ip = 0._dp - B2 = sum(B(1,:)*B(1,:) + B(2,:)*B(2,:)) DO k=1,n DO l=1,3 - DO m=1,3 - NF_ip(k,l) = NF_ip(k,l) - (R_ip*(B(1,l)*B(1,m)))*dBasisdx(k,m) - END DO - NF_ip(k,l) = NF_ip(k,l) + (R_ip*B2-w_dens)*dBasisdx(k,l) + val = SUM(dBasisdx(k,1:3)*B(1,1:3)) + NF_ip(k,l) = NF_ip(k,l) - SUM(REAL(Nu(l,1:3)) * B(1,1:3)) * val + & + (HdotB-w_dens)*dBasisdx(k,l) END DO END DO IF (.NOT. RealField) THEN DO k=1,n DO l=1,3 - DO m=1,3 - NF_ip(k,l) = NF_ip(k,l) - (R_ip*(B(2,l)*B(2,m)))*dBasisdx(k,m) - END DO + val = SUM(dBasisdx(k,1:3)*B(2,1:3)) + NF_ip(k,l) = NF_ip(k,l) - SUM(REAL(Nu(l,1:3)) * B(2,1:3)) * val END DO END DO END IF END IF - IF(ASSOCIATED(HB) .AND. RealField) THEN - Energy = Energy + s*(0.5*PR_ip*SUM(E**2) + w_dens) - ELSE - Energy = Energy + s*0.5*(PR_ip*SUM(E**2) + R_ip*SUM(B**2)) - END IF + Energy(1) = Energy(1) + s*0.5*PR_ip*SUM(E**2) + Energy(2) = Energy(2) + s*w_dens + Energy(3) = Energy(3) + (HdotB - w_dens) * s DO p=1,n DO q=1,n @@ -1383,13 +1460,20 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) IF ( (ASSOCIATED(MFS).OR.ASSOCIATED(EL_MFS)) .and. .not. HasZirka) THEN IF(.NOT. HasZirka) then - FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3)+s*(R_ip*B(1,:)-REAL(MG_ip))*Basis(p) - k = k+3 - IF ( Vdofs>1 ) THEN - FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3)+s*(R_ip*B(2,:)-AIMAG(MG_ip))*Basis(p) + IF (RealField) THEN + FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3) + & + s * (MATMUL(REAL(Nu), B(1,:)) - REAL(MG_ip)) * Basis(p) + k = k+3 + ELSE + FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3) + s * & + (MATMUL(REAL(Nu), B(1,:)) - MATMUL(AIMAG(Nu), B(2,:)) - REAL(MG_ip)) * Basis(p) + k = k+3 + FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3) + s * & + (MATMUL(AIMAG(Nu), B(1,:)) + MATMUL(REAL(Nu), B(2,:)) - AIMAG(MG_ip)) * Basis(p) k = k+3 END IF ELSE + ! Never here? FORCE(p,k+1:k+3) = FORCE(p,k+1:k+3)-s*(REAL(MG_ip))*Basis(p) END IF END IF @@ -1846,8 +1930,10 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) ! Perform parallel reductions Power = ParallelReduction(Power) - Energy = ParallelReduction(Energy) - + Energy(1) = ParallelReduction(Energy(1)) + Energy(2) = ParallelReduction(Energy(2)) + Energy(3) = ParallelReduction(Energy(3)) + IF (LossEstimation) THEN DO j=1,2 DO i=1,2 @@ -1868,11 +1954,24 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) CALL Info( 'MagnetoDynamicsCalcFields', Message ) CALL ListAddConstReal( Model % Simulation, 'res: Eddy current power', Power ) - WRITE(Message,*) '(Electro)Magnetic Field Energy: ', Energy - CALL Info( 'MagnetoDynamicsCalcFields', Message ) - CALL ListAddConstReal(Model % Simulation,'res: Magnetic Field Energy',Energy) + IF ( ListGetLogical( SolverParams,'Separate Magnetic Energy',Found ) ) THEN + WRITE(Message,'(A,ES12.3)') 'Electric Field Energy: ', Energy(1) + CALL Info( 'MagnetoDynamicsCalcFields', Message ) + WRITE(Message,'(A,ES12.3)') 'Magnetic Field Energy: ', Energy(2) + CALL Info( 'MagnetoDynamicsCalcFields', Message ) + WRITE(Message,'(A,ES12.3)') 'Magnetic Coenergy: ', Energy(3) + CALL Info( 'MagnetoDynamicsCalcFields', Message ) + CALL ListAddConstReal(Model % Simulation,'res: Electric Field Energy',Energy(1)) + CALL ListAddConstReal(Model % Simulation,'res: Magnetic Field Energy',Energy(2)) + CALL ListAddConstReal(Model % Simulation,'res: Magnetic Coenergy',Energy(3)) + ELSE + WRITE(Message,'(A,ES12.3)') 'ElectroMagnetic Field Energy: ',SUM(Energy(1:2)) + CALL Info( 'MagnetoDynamicsCalcFields', Message ) + CALL ListAddConstReal(Model % Simulation,'res: ElectroMagnetic Field Energy',SUM(Energy(1:2))) + END IF + IF(ALLOCATED(Gforce)) DEALLOCATE(Gforce) - DEALLOCATE( MASS,FORCE,Tcoef,RotM, R_t ) + DEALLOCATE( MASS,FORCE,Tcoef,RotM ) IF (LossEstimation) THEN CALL ListAddConstReal( Model % Simulation,'res: harmonic loss linear',TotalLoss(1) ) @@ -2078,10 +2177,10 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) END SELECT IF (.NOT. ActiveBoundaryElement(Element)) CYCLE - C = GetConstReal(BC, 'Layer Electric Conductivity', Found) - IF (ANY(ABS(C(1:n)) > AEPS)) THEN - R = GetConstReal(BC, 'Layer Relative Permeability', Found) - IF (.NOT. Found) R = 1.0_dp + C_ip = GetConstReal(BC, 'Layer Electric Conductivity', Found) + IF (ABS(C_ip) > AEPS) THEN + mu_r = GetConstReal(BC, 'Layer Relative Permeability', Found) + IF (.NOT. Found) mu_r = 1.0_dp ELSE CYCLE END IF @@ -2108,10 +2207,7 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) CALL GetEdgeBasis(Element, WBasis, RotWBasis, Basis, dBasisdx) END IF - C_ip = SUM(Basis(1:n) * C(1:n)) - R_ip = SUM(Basis(1:n) * R(1:n)) - R_ip = 4.0d0 * PI * 1d-7 * R_ip - val = SQRT(2.0_dp/(C_ip * Omega * R_ip)) ! The layer thickness + val = SQRT(2.0_dp/(C_ip * Omega * 4.0d0 * PI * 1d-7 * mu_r)) ! The layer thickness Zs = CMPLX(1.0_dp, 1.0_dp, KIND=dp) / (C_ip*val) E(1,:) = Omega * MATMUL(SOL(2,np+1:nd), WBasis(1:nd-np,:)) - MATMUL(SOL(1,1:np), dBasisdx(1:np,:)) @@ -2252,8 +2348,11 @@ SUBROUTINE MagnetoDynamicsCalcFields(Model,Solver,dt,Transient) DEALLOCATE(ThinLineCrossect, ThinLineCond) END IF - - + IF( NormIndex > 0 ) THEN + WRITE(Message,*) 'Reverting norm to: ', SaveNorm + CALL Info( 'MagnetoDynamicsCalcFields', Message ) + Solver % Variable % Norm = SaveNorm + END IF CONTAINS @@ -2529,7 +2628,7 @@ SUBROUTINE CalcBoundaryModels( ) END DO END IF - Energy = Energy + GapLength_ip*s*0.5*R_ip*B2 + Energy(2) = Energy(2) + GapLength_ip*s*0.5*R_ip*B2 DO p=1,n IF(HasLeft) LeftFORCE(LeftMap(p), 1:3) = LeftFORCE(LeftMap(p), 1:3) + s*NF_ip_l(p,1:3) @@ -2771,7 +2870,9 @@ SUBROUTINE GlobalSol(Var, m, b, dofs ) Solver % Variable % Values=0 Norm = DefaultSolve() var % Values(i::m) = Solver % Variable % Values - END DO + + IF( NormIndex == dofs ) SaveNorm = Norm + END DO !------------------------------------------------------------------------------ END SUBROUTINE GlobalSol !------------------------------------------------------------------------------ diff --git a/fem/src/modules/MagnetoDynamics/Utils.F90 b/fem/src/modules/MagnetoDynamics/Utils.F90 index 07fdcda490..c6bb2f83c5 100644 --- a/fem/src/modules/MagnetoDynamics/Utils.F90 +++ b/fem/src/modules/MagnetoDynamics/Utils.F90 @@ -35,9 +35,7 @@ ! *****************************************************************************/ !------------------------------------------------------------------------------ -!> Solve Maxwell equations in vector potential formulation (or the A-V -!> formulation) and (relatively)low frequency approximation using lowest -!> order Withney 1-forms (edge elements). +!> Utilities for the A-V solvers of electromagnetism !> \ingroup Solvers !------------------------------------------------------------------------------- MODULE MagnetoDynamicsUtils @@ -249,18 +247,19 @@ SUBROUTINE GetReluctivityR(Material,Acoef,n) !------------------------------------------------------------------------------ IMPLICIT NONE TYPE(ValueList_t), POINTER :: Material - INTEGER :: n REAL(KIND=dp) :: Acoef(:) + INTEGER :: n !------------------------------------------------------------------------------ - LOGICAL :: Found, FirstTime = .TRUE., Warned = .FALSE. + LOGICAL :: Found, FirstTime = .TRUE. REAL(KIND=dp) :: Avacuum - SAVE Avacuum + SAVE FirstTime, Avacuum +!------------------------------------------------------------------------------ IF ( FirstTime ) THEN Avacuum = GetConstReal( CurrentModel % Constants, & 'Permeability of Vacuum', Found ) - IF(.NOT. Found ) Avacuum = PI * 4.0d-7 + IF (.NOT. Found ) Avacuum = PI * 4.0d-7 FirstTime = .FALSE. END IF @@ -275,10 +274,9 @@ SUBROUTINE GetReluctivityR(Material,Acoef,n) ELSE Acoef(1:n) = GetReal( Material, 'Reluctivity', Found ) END IF - IF( .NOT. Found .AND. .NOT. Warned .AND. & + IF( .NOT. Found .AND. & .NOT. ListCheckPresent(Material, 'H-B Curve') ) THEN CALL Fatal('GetReluctivityR','Give > Relative Permeability < or > Reluctivity < for material!') - Warned = .TRUE. END IF !------------------------------------------------------------------------------ @@ -291,13 +289,14 @@ SUBROUTINE GetReluctivityC(Material,Acoef,n) !------------------------------------------------------------------------------ IMPLICIT NONE TYPE(ValueList_t), POINTER :: Material - INTEGER :: n COMPLEX(KIND=dp) :: Acoef(:) + INTEGER :: n !------------------------------------------------------------------------------ - LOGICAL :: L, Found, FirstTime = .TRUE., Warned = .FALSE. + LOGICAL :: L, Found, FirstTime = .TRUE. REAL(KIND=dp) :: Avacuum - SAVE Avacuum + SAVE Avacuum, FirstTime +!------------------------------------------------------------------------------ IF ( FirstTime ) THEN Avacuum = GetConstReal( CurrentModel % Constants, & @@ -320,81 +319,96 @@ SUBROUTINE GetReluctivityC(Material,Acoef,n) GetReal( Material, 'Reluctivity im', L ), KIND=dp ) Found = Found .OR. L END IF - IF( .NOT. Found .AND. .NOT. Warned .AND. & + IF( .NOT. Found .AND. & .NOT. ListCheckPresent(Material, 'H-B Curve') ) THEN CALL Fatal('GetReluctivityC','Give > Relative Permeability < or > Reluctivity < for material!') - Warned = .TRUE. END IF !------------------------------------------------------------------------------ END SUBROUTINE GetReluctivityC !------------------------------------------------------------------------------ -!> Get real tensorial reluctivity +!> Get a real-valued reluctivity tensor. This subroutine seeks values which +!> are strictly given as reluctivity (giving the permeability is not an option +!> here). !------------------------------------------------------------------------------ SUBROUTINE GetReluctivityTensorR(Material, Acoef, n, Found) !------------------------------------------------------------------------------- IMPLICIT NONE TYPE(ValueList_t), POINTER, INTENT(IN) :: Material - REAL(KIND=dp), POINTER :: Acoef(:,:,:) + REAL(KIND=dp), POINTER, INTENT(OUT) :: Acoef(:,:,:) INTEGER, INTENT(IN) :: n - LOGICAL , INTENT(OUT) :: Found -!------------------------------------------------------------------------------- - LOGICAL :: FirstTime = .FALSE. - INTEGER :: k - REAL(KIND=dp) :: Avacuum + LOGICAL, INTENT(OUT) :: Found +!------------------------------------------------------------------------------ + REAL(KIND=dp), SAVE :: nu_vacuum + LOGICAL, SAVE :: FirstTime +!------------------------------------------------------------------------------ - SAVE Avacuum + IF ( FirstTime ) THEN + nu_vacuum = GetConstReal( CurrentModel % Constants, & + 'Permeability of Vacuum', Found ) + IF (.NOT. Found ) THEN + nu_vacuum = 1.0d0/(PI * 4.0d-7) + ELSE + nu_vacuum = 1.0d0/nu_vacuum + END IF + FirstTime = .FALSE. + END IF CALL GetRealArray( Material, Acoef, 'Reluctivity', Found ) - ! - ! Earlier versions used 'Relative Reluctivity' although 'Relative' appears - ! to lack a physical meaning. For backward compatibility seek for - ! the old keyword command if needed: - ! - IF (.NOT. Found) CALL GetRealArray( Material, Acoef, 'Relative Reluctivity', Found ) + + IF (.NOT. Found) THEN + CALL GetRealArray( Material, Acoef, 'Relative Reluctivity', Found ) + IF (Found) Acoef = nu_vacuum * Acoef + END IF !------------------------------------------------------------------------------- END SUBROUTINE GetReluctivityTensorR !------------------------------------------------------------------------------- -!> Get complex tensorial reluctivity -!> Untested +!> Get a complex-valued reluctivity tensor. This subroutine seeks values which +!> are strictly given as reluctivity (giving the permeability is not an option +!> here). !------------------------------------------------------------------------------ - SUBROUTINE GetReluctivityTensorC(Material, Acoef, n, Found, Cwrk) + SUBROUTINE GetReluctivityTensorC(Material, Acoef, n, Found) !------------------------------------------------------------------------------- IMPLICIT NONE TYPE(ValueList_t), POINTER, INTENT(IN) :: Material - COMPLEX(KIND=dp), POINTER :: Acoef(:,:,:) - REAL(KIND=dp), POINTER, OPTIONAL :: Cwrk(:,:,:) - INTEGER, INTENT(IN) :: n - LOGICAL , INTENT(OUT) :: Found + COMPLEX(KIND=dp), POINTER, INTENT(OUT) :: Acoef(:,:,:) + INTEGER, INTENT(IN) :: n ! An inactive variable + LOGICAL, INTENT(OUT) :: Found !------------------------------------------------------------------------------- - LOGICAL :: FirstTime = .FALSE. LOGICAL :: Found_im - INTEGER :: k1,k2,k3 - REAL(KIND=dp) :: Avacuum - REAL(KIND=dp), POINTER :: work(:,:,:) - - SAVE Avacuum + REAL(KIND=dp), POINTER :: work(:,:,:) => NULL() + INTEGER :: n1, n2, n3 - IF(.NOT. PRESENT(Cwrk)) THEN - ALLOCATE(work(size(Acoef,1), size(Acoef,2), size(Acoef,3))) - ELSE - work => Cwrk - END IF + IF (ASSOCIATED(Acoef)) DEALLOCATE(Acoef) + CALL GetRealArray( Material, work, 'Reluctivity', Found ) - CALL GetRealArray( Material, work, 'Relative Reluctivity', Found ) - Acoef(:,:,:) = work(:,:,:) - - CALL GetRealArray( Material, work, 'Relative Reluctivity im', Found_im ) - - Acoef = CMPLX(REAL(Acoef), work) + IF (Found) THEN + n1 = SIZE(work,1) + n2 = SIZE(work,2) + n3 = SIZE(work,3) + ALLOCATE(Acoef(n1, n2, n3)) + Acoef(:,:,:) = CMPLX(work(:,:,:), 0.0d0, kind=dp) + END IF + CALL GetRealArray( Material, work, 'Reluctivity im', Found_im ) + IF (Found_im) THEN + n1 = SIZE(work,1) + n2 = SIZE(work,2) + n3 = SIZE(work,3) + IF (.NOT. ASSOCIATED(Acoef)) THEN + ALLOCATE(Acoef(n1, n2, n3)) + Acoef(:,:,:) = CMPLX(0.0d0, work(:,:,:), kind=dp) + ELSE + IF (SIZE(Acoef,1) /= n1 .OR. SIZE(Acoef,2) /= n2 .OR. SIZE(Acoef,3) /= n3) & + CALL Fatal('GetReluctivityTensorC', 'Reluctivity and Reluctivity im of different size') + Acoef(1:n1,1:n2,1:n3) = CMPLX(REAL(Acoef(1:n1,1:n2,1:n3)), work(1:n1,1:n2,1:n3), kind=dp) + END IF + END IF Found = Found .OR. Found_im - IF(.NOT. PRESENT(Cwrk)) THEN - DEALLOCATE(work) - END IF + IF (ASSOCIATED(work)) DEALLOCATE(work) !------------------------------------------------------------------------------- END SUBROUTINE GetReluctivityTensorC !------------------------------------------------------------------------------- @@ -409,13 +423,14 @@ SUBROUTINE GetPermittivity(Material,Acoef,n) !------------------------------------------------------------------------------ LOGICAL :: Found, FirstTime = .TRUE., Warned = .FALSE. REAL(KIND=dp) :: Pvacuum = 0._dp + SAVE FirstTime, Warned, Pvacuum +!------------------------------------------------------------------------------ IF ( FirstTime ) THEN Pvacuum = GetConstReal( CurrentModel % Constants, & 'Permittivity of Vacuum', Found ) FirstTime = .FALSE. END IF - Acoef(1:n) = GetReal( Material, 'Relative Permittivity', Found ) IF ( Found ) THEN diff --git a/fem/src/modules/MagnetoDynamics/WhitneyAVHarmonicSolver.F90 b/fem/src/modules/MagnetoDynamics/WhitneyAVHarmonicSolver.F90 index 58da33338a..e671e9dcc5 100644 --- a/fem/src/modules/MagnetoDynamics/WhitneyAVHarmonicSolver.F90 +++ b/fem/src/modules/MagnetoDynamics/WhitneyAVHarmonicSolver.F90 @@ -107,12 +107,12 @@ END SUBROUTINE WhitneyAVHarmonicSolver_Init !------------------------------------------------------------------------------ -!> Solve vector potential A, scale potential V +!> Solve a vector potential A and scalar potential V from ! !> j omega sigma A+rot (1/mu) rot A+sigma grad(V) = J^s+rot M^s-sigma grad(V^s) !> -div(sigma (j omega A+grad(V)))=0 ! -!> using edge elements (Nedelec/W basis of lowest degree) + nodal basis for V. +!> by using edge elements (Nedelec) + nodal basis for V. !> \ingroup Solvers !------------------------------------------------------------------------------ SUBROUTINE WhitneyAVHarmonicSolver( Model,Solver,dt,Transient ) @@ -130,47 +130,41 @@ SUBROUTINE WhitneyAVHarmonicSolver( Model,Solver,dt,Transient ) ! Local variables !------------------------------------------------------------------------------ LOGICAL :: AllocationsDone = .FALSE., Found, L1 - TYPE(Element_t),POINTER :: Element, Edge - - REAL(KIND=dp) :: Norm, Omega - TYPE(ValueList_t), POINTER :: BodyForce, Material, BC, BodyParams, SolverParams + LOGICAL :: Stat, EigenAnalysis, TG, Jfix, JfixSolve, LaminateStack, CoilBody, EdgeBasis,LFact,LFactFound + LOGICAL :: PiolaVersion, SecondOrder, GotHbCurveVar, HasTensorReluctivity + LOGICAL :: ExtNewton + LOGICAL, ALLOCATABLE, SAVE :: TreeEdges(:) INTEGER :: n,nb,nd,t,istat,i,j,k,l,nNodes,Active,FluxCount=0 INTEGER :: NoIterationsMin, NoIterationsMax - - TYPE(Mesh_t), POINTER :: Mesh + INTEGER :: NewtonIter + INTEGER, POINTER :: Perm(:) + INTEGER, ALLOCATABLE :: FluxMap(:) COMPLEX(kind=dp) :: Aval COMPLEX(KIND=dp), ALLOCATABLE :: STIFF(:,:), MASS(:,:), FORCE(:), JFixFORCE(:),JFixVec(:,:) COMPLEX(KIND=dp), ALLOCATABLE :: LOAD(:,:), Acoef(:), Tcoef(:,:,:) COMPLEX(KIND=dp), ALLOCATABLE :: LamCond(:) + COMPLEX(KIND=dp), POINTER :: Acoef_t(:,:,:) => NULL() + REAL(KIND=dp) :: Norm, Omega REAL(KIND=dp), ALLOCATABLE :: RotM(:,:,:), GapLength(:), MuParameter(:), SkinCond(:) - - REAL (KIND=DP), POINTER :: Cwrk(:,:,:), Cwrk_im(:,:,:), LamThick(:) - + REAL(KIND=dp), POINTER :: Cwrk(:,:,:), Cwrk_im(:,:,:), LamThick(:) REAL(KIND=dp), POINTER :: sValues(:), fixpot(:) - TYPE(Variable_t), POINTER :: jfixvar, jfixvarIm, HbCurveVar + REAL(KIND=dp) :: NewtonTol CHARACTER(LEN=MAX_NAME_LEN):: LaminateStackModel, CoilType, HbCurveVarName - LOGICAL :: Stat, EigenAnalysis, TG, Jfix, JfixSolve, LaminateStack, CoilBody, EdgeBasis,LFact,LFactFound - LOGICAL :: PiolaVersion, SecondOrder, GotHbCurveVar - REAL(KIND=dp) :: NewtonTol - INTEGER :: NewtonIter - LOGICAL :: ExtNewton - - INTEGER, POINTER :: Perm(:) - INTEGER, ALLOCATABLE :: FluxMap(:) - LOGICAL, ALLOCATABLE, SAVE :: TreeEdges(:) - + TYPE(Mesh_t), POINTER :: Mesh + TYPE(Element_t),POINTER :: Element, Edge + TYPE(ValueList_t), POINTER :: BodyForce, Material, BC, BodyParams, SolverParams + TYPE(Variable_t), POINTER :: jfixvar, jfixvarIm, HbCurveVar TYPE(Matrix_t), POINTER :: A TYPE(ListMatrix_t), POINTER :: BasicCycles(:) - TYPE(ValueList_t), POINTER :: CompParams SAVE STIFF, LOAD, MASS, FORCE, Tcoef, JFixVec, JFixFORCE, & - Acoef, Cwrk, Cwrk_im, LamCond, & + Acoef, Acoef_t, Cwrk, Cwrk_im, LamCond, & LamThick, AllocationsDone, RotM, & GapLength, MuParameter, SkinCond !------------------------------------------------------------------------------ @@ -372,8 +366,18 @@ FUNCTION DoSolve(IterNo) RESULT(Converged) Tcoef = 0.0_dp Material => GetMaterial( Element ) IF ( ASSOCIATED(Material) ) THEN - CALL GetReluctivity(Material,Acoef,n) - + HasTensorReluctivity = .FALSE. + CALL GetReluctivity(Material,Acoef_t,n,HasTensorReluctivity) + IF (HasTensorReluctivity) THEN + IF (size(Acoef_t,1)==1 .AND. size(Acoef_t,2)==1) THEN + Acoef(1:n) = Acoef_t(1,1,1:n) + HasTensorReluctivity = .FALSE. + ELSE IF (size(Acoef_t,1)/=3) THEN + CALL Fatal('WhitneyAVHarmonicSolver', 'Reluctivity tensor should be of size 3x3') + END IF + ELSE + CALL GetReluctivity(Material,Acoef,n) + END IF !------------------------------------------------------------------------------ ! Read conductivity values (might be a tensor) !------------------------------------------------------------------------------ @@ -1053,32 +1057,31 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & LOGICAL :: PiolaVersion, SecondOrder !------------------------------------------------------------------------------ REAL(KIND=dp) :: WBasis(nd,3), RotWBasis(nd,3) - COMPLEX(KIND=dp) :: mu, C(3,3), L(3), G(3), M(3), JfixPot(n), Nu(3,3) REAL(KIND=dp) :: Basis(n),dBasisdx(n,3),DetJ, & RotMLoc(3,3), RotM(3,3,n), velo(3), omega_velo(3,n), & lorentz_velo(3,n), RotWJ(3) + REAL(KIND=dp) :: LocalLamThick, skind, babs, muder, AlocR(2,nd) + REAL(KIND=dp) :: nu_11(nd), nuim_11(nd), nu_22(nd), nuim_22(nd) + REAL(KIND=dp) :: nu_val, nuim_val + REAL(KIND=dp), POINTER :: Bval(:), Hval(:), Cval(:), & + CubicCoeff(:)=>NULL(),HB(:,:)=>NULL() + COMPLEX(KIND=dp) :: mu, C(3,3), L(3), G(3), M(3), JfixPot(n), Nu(3,3) COMPLEX(KIND=dp) :: LocalLamCond, JAC(nd,nd), B_ip(3), Aloc(nd), & CVelo(3), CVeloSum - REAL(KIND=dp) :: LocalLamThick, skind, babs, muder, AlocR(2,nd) CHARACTER(LEN=MAX_NAME_LEN):: LaminateStackModel, CoilType LOGICAL :: Stat, LaminateStack, Newton, Cubic, HBCurve, CoilBody, & HasVelocity, HasLorenzVelocity, HasAngularVelocity + LOGICAL :: StrandedHomogenization, FoundIm + INTEGER :: t, i, j, p, q, np, siz, EdgeBasisDegree - TYPE(GaussIntegrationPoints_t) :: IP - REAL(KIND=dp), POINTER :: Bval(:), Hval(:), Cval(:), & - CubicCoeff(:)=>NULL(),HB(:,:)=>NULL() + TYPE(GaussIntegrationPoints_t) :: IP TYPE(ValueListEntry_t), POINTER :: Lst - TYPE(Nodes_t), SAVE :: Nodes - TYPE(ValueList_t), POINTER :: CompParams - LOGICAL :: StrandedHomogenization, FoundIm - REAL(KIND=dp) :: nu_11(nd), nuim_11(nd), nu_22(nd), nuim_22(nd) - REAL(KIND=dp) :: nu_val, nuim_val !------------------------------------------------------------------------------ IF (SecondOrder) THEN EdgeBasisDegree = 2 @@ -1191,7 +1194,6 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & CALL GetEdgeBasis(Element, WBasis, RotWBasis, Basis, dBasisdx) END IF - mu = SUM( Basis(1:n) * Acoef(1:n) ) ! Compute convection type term coming from rotation ! ------------------------------------------------- @@ -1234,6 +1236,8 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & IF ( Newton ) THEN muder=(DerivateCurve(Bval,Hval,Babs,CubicCoeff=Cval)-mu)/babs END IF + ELSE + mu = SUM( Basis(1:n) * Acoef(1:n) ) END IF IF (LaminateStack) THEN @@ -1251,20 +1255,35 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & END SELECT END IF - Nu = CMPLX(0._dp, 0._dp) - Nu(1,1) = mu - Nu(2,2) = mu - Nu(3,3) = mu - - IF (CoilBody .AND. StrandedHomogenization) THEN - nu_val = SUM( Basis(1:n) * nu_11(1:n) ) - nuim_val = SUM( Basis(1:n) * nuim_11(1:n) ) - Nu(1,1) = CMPLX(nu_val, nuim_val, KIND=dp) - nu_val = SUM( Basis(1:n) * nu_22(1:n) ) - nuim_val = SUM( Basis(1:n) * nuim_22(1:n) ) - Nu(2,2) = CMPLX(nu_val, nuim_val, KIND=dp) - Nu = MATMUL(MATMUL(RotMLoc, Nu),TRANSPOSE(RotMLoc)) - END IF + IF (HasTensorReluctivity) THEN + IF (SIZE(Acoef_t,2) == 1) THEN + Nu = CMPLX(0._dp, 0._dp, kind=dp) + DO i = 1,3 + Nu(i,i) = SUM(Basis(1:n)*Acoef_t(i,1,1:n)) + END DO + ELSE + DO i = 1,3 + DO j = 1,3 + Nu(i,j) = SUM(Basis(1:n)*Acoef_t(i,j,1:n)) + END DO + END DO + END IF + ELSE + Nu = CMPLX(0._dp, 0._dp, kind=dp) + Nu(1,1) = mu + Nu(2,2) = mu + Nu(3,3) = mu + + IF (CoilBody .AND. StrandedHomogenization) THEN + nu_val = SUM( Basis(1:n) * nu_11(1:n) ) + nuim_val = SUM( Basis(1:n) * nuim_11(1:n) ) + Nu(1,1) = CMPLX(nu_val, nuim_val, KIND=dp) + nu_val = SUM( Basis(1:n) * nu_22(1:n) ) + nuim_val = SUM( Basis(1:n) * nuim_22(1:n) ) + Nu(2,2) = CMPLX(nu_val, nuim_val, KIND=dp) + Nu = MATMUL(MATMUL(RotMLoc, Nu),TRANSPOSE(RotMLoc)) + END IF + END IF M = MATMUL( LOAD(4:6,1:n), Basis(1:n) ) L = MATMUL( LOAD(1:3,1:n), Basis(1:n) ) @@ -1286,7 +1305,7 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & L = L - MATMUL(JfixPot, dBasisdx(1:n,:)) END IF END IF - + ! Compute element stiffness matrix and force vector: ! -------------------------------------------------- @@ -1305,7 +1324,7 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & ! matrix (anisotropy taken into account) ! ------------------------------------------- IF ( SUM(C) /= 0._dp ) THEN - STIFF(p,q) = STIFF(p,q) + SUM(MATMUL(C, dBasisdx(p,:)) * dBasisdx(q,:))*detJ*IP % s(t) + STIFF(p,q) = STIFF(p,q) + SUM(MATMUL(C, dBasisdx(q,:)) * dBasisdx(p,:))*detJ*IP % s(t) END IF END DO DO j=1,nd-np @@ -1366,7 +1385,7 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & END IF STIFF(p,q) = STIFF(p,q) + & - SUM(MATMUL(Nu, RotWBasis(i,:))*RotWBasis(j,:))*detJ*IP%s(t) + SUM(MATMUL(Nu, RotWBasis(j,:))*RotWBasis(i,:))*detJ*IP%s(t) ! Compute the conductivity term ! for stiffness matrix (anisotropy taken into account) diff --git a/fem/src/modules/MagnetoDynamics/WhitneyAVSolver.F90 b/fem/src/modules/MagnetoDynamics/WhitneyAVSolver.F90 index e887db2739..399ffd845f 100644 --- a/fem/src/modules/MagnetoDynamics/WhitneyAVSolver.F90 +++ b/fem/src/modules/MagnetoDynamics/WhitneyAVSolver.F90 @@ -231,7 +231,7 @@ SUBROUTINE WhitneyAVSolver( Model,Solver,dt,Transient ) TYPE(Mesh_t), POINTER :: Mesh REAL(KIND=dp), POINTER :: VecPot(:) - REAL(KIND=dp), POINTER :: Cwrk(:,:,:), Acoef_t(:,:,:) + REAL(KIND=dp), POINTER :: Cwrk(:,:,:), Acoef_t(:,:,:) => NULL() REAL(KIND=dp), ALLOCATABLE :: LOAD(:,:), Acoef(:), Tcoef(:,:,:), & GapLength(:), AirGapMu(:), LamThick(:), & LamCond(:), Wbase(:), RotM(:,:,:), & @@ -372,14 +372,14 @@ SUBROUTINE WhitneyAVSolver( Model,Solver,dt,Transient ) IF(ALLOCATED(FORCE)) THEN DEALLOCATE(FORCE, JFixFORCE, JFixVec, LOAD, STIFF, MASS, TCoef, GapLength, AirGapMu, & - Acoef, LamThick, LamCond, WBase, RotM, DConstr, Acoef_t,ThinLineCrossect, ThinLineCond ) + Acoef, LamThick, LamCond, WBase, RotM, DConstr, ThinLineCrossect, ThinLineCond ) END IF ALLOCATE( FORCE(N), JFixFORCE(n), JFixVec(3,n), LOAD(7,N), STIFF(N,N), & MASS(N,N), Tcoef(3,3,N), GapLength(N), & AirGapMu(N), Acoef(N), LamThick(N), & LamCond(N), Wbase(N), RotM(3,3,N), & - DConstr(N,N), Acoef_t(3,3,N), & + DConstr(N,N), & ThinLineCrossect(N), ThinLineCond(N), STAT=istat ) IF ( istat /= 0 ) THEN CALL Fatal( 'WhitneyAVSolver', 'Memory allocation error.' ) @@ -621,7 +621,6 @@ LOGICAL FUNCTION DoSolve(IterNo) RESULT(Converged) END IF Acoef = 0.0d0 - Acoef_t = 0.0d0 Tcoef = 0.0d0 Material => GetMaterial( Element ) IF ( ASSOCIATED(Material) ) THEN @@ -636,6 +635,10 @@ LOGICAL FUNCTION DoSolve(IterNo) RESULT(Converged) ELSE CALL GetReluctivity(Material,Acoef,n) END IF + IF (HasTensorReluctivity) THEN + IF (size(Acoef_t,1)/=3) CALL Fatal('WhitneyAVSolver', & + 'Reluctivity tensor should be of size 3x3') + END IF !------------------------------------------------------------------------------ ! Read conductivity values (might be a tensor) !------------------------------------------------------------------------------ @@ -1500,7 +1503,7 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & LOGICAL :: PiolaVersion, SecondOrder !------------------------------------------------------------------------------ REAL(KIND=dp) :: Aloc(nd), JAC(nd,nd), mu, muder, B_ip(3), Babs - REAL(KIND=dp) :: WBasis(nd,3), RotWBasis(nd,3), A, Acoefder(n), C(3,3), & + REAL(KIND=dp) :: WBasis(nd,3), RotWBasis(nd,3), Acoefder(n), C(3,3), & RotMLoc(3,3), RotM(3,3,n), velo(3), omega(3), omega_velo(3,n), & lorentz_velo(3,n), VeloCrossW(3), RotWJ(3), CVelo(3), & A_t(3,3) @@ -1604,15 +1607,19 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & CALL GetEdgeBasis(Element, WBasis, RotWBasis, Basis, dBasisdx) END IF - A = SUM( Basis(1:n) * Acoef(1:n) ) - mu = A - - IF(HasTensorReluctivity) THEN - DO i = 1,3 - DO j = 1,3 - A_t(i,j) = sum(Basis(1:n)*Acoef_t(i,j,1:n)) + IF (HasTensorReluctivity) THEN + IF (SIZE(Acoef_t,2) == 1) THEN + A_t = 0.0d0 + DO i = 1,3 + A_t(i,i) = SUM(Basis(1:n)*Acoef_t(i,1,1:n)) END DO - END DO + ELSE + DO i = 1,3 + DO j = 1,3 + A_t(i,j) = SUM(Basis(1:n)*Acoef_t(i,j,1:n)) + END DO + END DO + END IF END IF ! Compute convection type term coming from a rigid motion: @@ -1683,6 +1690,7 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & muder=(DerivateCurve(Bval,Hval,Babs,CubicCoeff=Cval)-mu)/babs END IF ELSE + mu = SUM( Basis(1:n) * Acoef(1:n) ) muder = 0._dp END IF @@ -1810,12 +1818,12 @@ SUBROUTINE LocalMatrix( MASS, STIFF, FORCE, JFixFORCE, JFixVec, LOAD, & SUM(M*RotWBasis(i,:)))*detJ*IP%s(t) DO j = 1,nd-np q = j+np - STIFF(p,q) = STIFF(p,q) + mu * SUM(RotWBasis(i,:)*RotWBasis(j,:))*detJ*IP%s(t) - ! Aniostropic part - IF(HasTensorReluctivity) THEN + IF (HasTensorReluctivity) THEN STIFF(p,q) = STIFF(p,q) & - + SUM(RotWBasis(i,:) * MATMUL(A_t, RotWBasis(j,:)))*detJ*IP%s(t) + + SUM(RotWBasis(i,:) * MATMUL(A_t, RotWBasis(j,:)))*detJ*IP%s(t) + ELSE + STIFF(p,q) = STIFF(p,q) + mu * SUM(RotWBasis(i,:)*RotWBasis(j,:))*detJ*IP%s(t) END IF IF ( Newton ) THEN JAC(p,q) = JAC(p,q) + muder * SUM(B_ip(:)*RotWBasis(j,:)) * & diff --git a/fem/src/modules/MagnetoDynamics2D.F90 b/fem/src/modules/MagnetoDynamics2D.F90 index a82d86ec1b..95fb67a084 100644 --- a/fem/src/modules/MagnetoDynamics2D.F90 +++ b/fem/src/modules/MagnetoDynamics2D.F90 @@ -973,11 +973,8 @@ SUBROUTINE MagnetoDynamics2DHarmonic_Init0( Model,Solver,dt,TransientSimulation REAL(KIND=dp) :: dt !< Timestep size for time dependent simulations LOGICAL :: TransientSimulation !< Steady state or transient simulation !------------------------------------------------------------------------------ - IF( .NOT.ListCheckPresent( Solver % Values, 'Apply Mortar BCs') ) & - CALL ListAddLogical( Solver % Values, 'Apply Mortar BCs', .TRUE.) - - IF( .NOT.ListCheckPresent( Solver % Values, 'Linear System Complex') ) & - CALL ListAddLogical( Solver % Values, 'Linear System Complex', .TRUE.) + CALL ListAddNewLogical( Solver % Values, 'Apply Mortar BCs', .TRUE.) + CALL ListAddNewLogical( Solver % Values, 'Linear System Complex', .TRUE.) !------------------------------------------------------------------------------ END SUBROUTINE MagnetoDynamics2DHarmonic_Init0 !------------------------------------------------------------------------------ @@ -997,18 +994,11 @@ SUBROUTINE MagnetoDynamics2DHarmonic_Init( Model,Solver,dt,TransientSimulation ) Params => GetSolverParams(Solver) CALL ListAddInteger( Params, 'Variable Dofs',2 ) - IF( .NOT. ListCheckPresent( Params,'Variable') ) THEN - CALL ListAddString( Params,'Variable',& - 'Potential[Potential re:1 Potential im:1]') - END IF + CALL ListAddNewString( Params,'Variable',& + 'Potential[Potential re:1 Potential im:1]') - IF(.NOT. ListCheckPresent( Params,'Apply Mortar BCs') ) THEN - CALL ListAddLogical( Params,'Apply Mortar BCs',.TRUE.) - END IF - - IF(.NOT. ListCheckPresent( Params,'Linear System Complex') ) THEN - CALL ListAddLogical( Params,'Linear System Complex',.TRUE.) - END IF + CALL ListAddNewLogical( Params,'Apply Mortar BCs',.TRUE.) + CALL ListAddNewLogical( Params,'Linear System Complex',.TRUE.) !------------------------------------------------------------------------------ END SUBROUTINE MagnetoDynamics2DHarmonic_Init @@ -1384,7 +1374,7 @@ SUBROUTINE Potential( U, A, Element,n,nd) CALL GetLocalSolution(POT, UElement=Element) POTC = POT(1,:) + im*POT(2,:) - Omega = GetAngularFrequency(Found=Found) + Omega = GetAngularFrequency() !Numerical integration: !---------------------- @@ -1451,9 +1441,10 @@ SUBROUTINE LocalMatrix( Element, n, nd) Material => GetMaterial(Element) - Omega = GetAngularFrequency(Found=Found) + Omega = GetAngularFrequency() + InPlaneProximity = .FALSE. - + CoilBody = .FALSE. CompParams => GetComponentParams( Element ) CoilType = '' @@ -1953,9 +1944,7 @@ SUBROUTINE Bsolver_init( Model,Solver,dt,Transient ) LOGICAL :: Found SolverParams => GetSolverParams() - IF( .NOT. ListCheckPresent( SolverParams,'Variable') ) THEN - CALL ListAddString( SolverParams, 'Variable','-nooutput bsolver_temp' ) - END IF + CALL ListAddNewString( SolverParams, 'Variable','-nooutput bsolver_temp' ) IF( GetLogical( SolverParams,'Target Variable Complex',Found ) ) THEN CALL ListAddString( SolverParams,& NextFreeKeyword('Exported Variable',SolverParams),'B[B re:2 B im:2]') @@ -2022,6 +2011,9 @@ SUBROUTINE Bsolver( Model,Solver,dt,Transient ) SAVE Visited + CALL Warn('BSolver','This module is obsolete! USE MagnetoDynamicsCalcFields instead') + + CALL Info( 'BSolver', '-------------------------------------',Level=4 ) CALL Info( 'BSolver', 'Computing the magnetic field density ',Level=4 ) CALL Info( 'BSolver', '-------------------------------------',Level=4 ) diff --git a/fem/src/modules/ResultOutputSolve/VtuOutputSolver.F90 b/fem/src/modules/ResultOutputSolve/VtuOutputSolver.F90 index e019362317..7724b9aaeb 100644 --- a/fem/src/modules/ResultOutputSolve/VtuOutputSolver.F90 +++ b/fem/src/modules/ResultOutputSolve/VtuOutputSolver.F90 @@ -851,7 +851,7 @@ SUBROUTINE WriteVtuFile( VtuFile, Model, RemoveDisp ) Params => GetSolverParams() Buffered = .TRUE. FlipActive = .FALSE. - + ! we could have huge amount of gauss points ALLOCATE( ElemInd(512)) !Model % Mesh % MaxElementDOFS)) @@ -1067,10 +1067,6 @@ SUBROUTINE WriteVtuFile( VtuFile, Model, RemoveDisp ) ! Some vectors are defined by a set of components (either 2 or 3) !--------------------------------------------------------------------- IF( ComponentVector ) THEN - !IF( NoModes + NoModes2 > 0 ) THEN - ! CALL Warn('WriteVtuXMLFile','Modes cannot currently be given componentwise!') - ! CYCLE - !END IF IF( VarType == Variable_on_gauss_points ) THEN CALL Warn('WriteVtuXMLFile','Gauss point variables cannot currently be given componentwise!') CYCLE @@ -1146,9 +1142,11 @@ SUBROUTINE WriteVtuFile( VtuFile, Model, RemoveDisp ) IF( ( DG .OR. DN ) .AND. VarType == Variable_on_nodes_on_elements ) THEN CALL Info('WriteVTUFile','Setting field type to discontinuous',Level=12) InvFieldPerm => InvDgPerm - ELSE + ELSE IF( ALLOCATED( InvNodePerm ) ) THEN CALL Info('WriteVTUFile','Setting field type to nodal',Level=14) InvFieldPerm => InvNodePerm + ELSE + InvFieldPerm => NULL() END IF IF(.NOT. EigenAnalysis ) THEN @@ -1212,6 +1210,12 @@ SUBROUTINE WriteVtuFile( VtuFile, Model, RemoveDisp ) !--------------------------------------------------------------------- IF( WriteData ) THEN + IF( .NOT. NoPermutation .AND. NumberOfDofNodes > 0 ) THEN + IF(.NOT. ASSOCIATED( InvFieldPerm ) ) THEN + CALL Fatal(Caller,'InvFieldPerm not associated!') + END IF + END IF + IF( BinaryOutput ) WRITE( VtuUnit ) k DO ii = 1, NumberOfDofNodes @@ -1506,7 +1510,7 @@ SUBROUTINE WriteVtuFile( VtuFile, Model, RemoveDisp ) END IF IF( n == 0 ) THEN - ElemVectVal(k) = 0.0_dp + ElemVectVal(1:sdofs) = 0.0_dp ELSE DO j=1,n ElemInd(j) = Perm(m)+j @@ -2178,7 +2182,7 @@ SUBROUTINE WritePvtuFile( PvtuFile, Model ) WRITE( FullName,'(A,I0)') TRIM( FieldName )//' EigenMode',IndField ! Note: this should be added for "HarmonicMode" and "ConstraintMode" too - ! now the .vptu file for these vectors is not correct! + ! now the .pvtu file for these vectors is not correct! END IF IF( AsciiOutput ) THEN diff --git a/fem/src/modules/SaveData/SaveScalars.F90 b/fem/src/modules/SaveData/SaveScalars.F90 index 19bd62c978..e155029718 100644 --- a/fem/src/modules/SaveData/SaveScalars.F90 +++ b/fem/src/modules/SaveData/SaveScalars.F90 @@ -1537,7 +1537,7 @@ SUBROUTINE AddToSaveList(Name, Val, ValueIsInteger, ParallelOperator ) CHARACTER(LEN=MAX_NAME_LEN), OPTIONAL :: ParallelOperator !------------------------------------------------------------------------ CHARACTER(LEN=MAX_NAME_LEN) :: Str, ParOper - REAL, ALLOCATABLE :: TmpValues(:) + REAL(KIND=dp), ALLOCATABLE :: TmpValues(:) CHARACTER(LEN=MAX_NAME_LEN), ALLOCATABLE :: TmpValueNames(:) LOGICAL, POINTER :: TmpValuesInteger(:) TYPE(Variable_t), POINTER :: TargetVar @@ -2023,7 +2023,7 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) REAL(KIND=dp) :: SqrtMetric,Metric(3,3),Symb(3,3,3),dSymb(3,3,3,3) REAL(KIND=dp) :: Basis(Model % MaxElementNodes), dBasisdx(Model % MaxElementNodes,3) REAL(KIND=dp) :: EnergyTensor(3,3,Model % MaxElementNodes),& - EnergyCoeff(Model % MaxElementNodes) + EnergyCoeff(Model % MaxElementNodes), ElemVals(Model % MaxElementNodes) REAL(KIND=dp) :: SqrtElementMetric,U,V,W,S,A,L,C(3,3),x,y,z REAL(KIND=dp) :: func, coeff, integral1, integral2, Grad(3), CoeffGrad(3) REAL(KIND=DP), POINTER :: Pwrk(:,:,:) @@ -2085,7 +2085,17 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) IF( .NOT. ListGetLogical( MaskList, MaskName, GotIt ) ) CYCLE END IF - + IF( NoDOFs == 1 ) THEN + ElemVals(1:n) = Var % Values(Var % Perm(PermIndexes) ) + ELSE + ElemVals(1:n) = 0.0_dp + DO i=1,NoDOFs + ElemVals(1:n) = ElemVals(1:n) + Var % Values(NoDofs*(Var % Perm(PermIndexes)-1)+i )**2 + END DO + ElemVals(1:) = SQRT(ElemVals(1:n)) + END IF + + k = ListGetInteger( Model % Bodies( Element % BodyId ) % Values, & 'Material', GotIt, minv=1, maxv=Model % NumberOfMaterials ) @@ -2163,26 +2173,26 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) integral1 = integral1 + coeff * S CASE ('int','int mean') - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) integral1 = integral1 + S * coeff * func CASE ('int square','int square mean','int rms') - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) integral1 = integral1 + S * coeff * func**2 CASE ('int abs','int abs mean') - func = ABS( SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) ) + func = ABS( SUM( ElemVals(1:n) * Basis(1:n) ) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) integral1 = integral1 + S * coeff * func CASE ('int variance') - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) integral1 = integral1 + S * coeff * func @@ -2191,7 +2201,7 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) CASE ('diffusive energy') CoeffGrad = 0.0d0 DO j = 1, DIM - Grad(j) = SUM( dBasisdx(1:n,j) * Var % Values(Var % Perm(PermIndexes)) ) + Grad(j) = SUM( dBasisdx(1:n,j) * ElemVals(1:n) ) DO k = 1, DIM CoeffGrad(j) = CoeffGrad(j) + SUM( EnergyTensor(j,k,1:n) * Basis(1:n) ) * & SUM( dBasisdx(1:n,k) * Var % Values(Var % Perm(PermIndexes)) ) @@ -2201,12 +2211,12 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) integral1 = integral1 + s * SUM( Grad(1:DIM) * CoeffGrad(1:DIM) ) CASE ('convective energy') - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) IF(NoDofs == 1) THEN - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) integral1 = integral1 + s * coeff * func**2 ELSE func = 0.0d0 @@ -2218,7 +2228,7 @@ FUNCTION BulkIntegrals(Var, OperName, GotCoeff, CoeffName) RESULT (operx) CASE ('potential energy') - func = SUM( Var % Values(Var % Perm(PermIndexes)) * Basis(1:n) ) + func = SUM( ElemVals(1:n) * Basis(1:n) ) IF( PosOper ) func = MAX( 0.0_dp, func ) IF( NegOper ) func = MIN( 0.0_dp, func ) diff --git a/fem/src/modules/ShellSolver.F90 b/fem/src/modules/ShellSolver.F90 index f480eb3187..ece98672a5 100755 --- a/fem/src/modules/ShellSolver.F90 +++ b/fem/src/modules/ShellSolver.F90 @@ -137,6 +137,7 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) !------------------------------------------------------------------------------ USE DefUtils USE ElementDescription + USE SolidMechanicsUtils IMPLICIT NONE !------------------------------------------------------------------------------ @@ -204,6 +205,7 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) LOGICAL :: MassAssembly, HarmonicAssembly LOGICAL :: Parallel LOGICAL :: SolidShellCoupling + LOGICAL :: DrillingDOFs, RotateDOFs INTEGER, POINTER :: Indices(:) => NULL() INTEGER, POINTER :: VisitsList(:) => NULL() @@ -224,6 +226,7 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) REAL(KIND=dp) :: PatchNodes(MaxPatchNodes,2), ZNodes(MaxPatchNodes) REAL(KIND=dp) :: BlendingSurfaceArea, ShellModelArea, MappedMeshArea, RefArea REAL(KIND=dp) :: NonlinTol, NonlinRes, NonlinRes0 + REAL(KIND=dp) :: DrillingPar CHARACTER(LEN=MAX_NAME_LEN) :: OutputFile @@ -275,6 +278,18 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) Bubbles = GetLogical(SolverPars, 'Bubbles', Found) + DrillingDOFs = GetLogical(SolverPars, 'Drilling DOFs', Found) + IF (DrillingDOFs) CALL Warn('ShellSolver', & + 'Drilling DOFs do not support all options and alters the meaning of all rotational DOFs/BCs') + IF (DrillingDOFs) THEN + DrillingPar = GetConstReal(SolverPars, 'Drilling Stabilization Parameter', Found) + IF (.NOT. Found) DrillingPar = 1.0d0 + ELSE + DrillingPar = 1.0d0 + END IF + + RotateDOFs = GetLogical(SolverPars, 'Rotate DOFs', Found) + !----------------------------------------------------------------------------------- ! The field variables for saving the orientation of lines of curvature basis ! vectors at the nodes: Since the global DOFs are expressed with respect to the @@ -378,6 +393,8 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) NonlinTol = GetConstReal(SolverPars, 'Nonlinear System Convergence Tolerance') IF (LargeDeflection) THEN + IF (DrillingDOFs) CALL Fatal('ShellSolver', & + 'Drilling DOFs cannot yet be combined with Large Deflection') SolveBenchmarkCase = .FALSE. IF (.NOT. ASSOCIATED(Solver % Matrix % BulkRHS)) & ALLOCATE(Solver % Matrix % BulkRHS(SIZE(Solver % Matrix % RHS))) @@ -479,8 +496,8 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) ! ----------------------------------------------------------------------------- CALL ShellLocalMatrix(BGElement, n, nd+nb, ShellModelPar, LocalSol, & LargeDeflection, StrainReductionMethod, MembraneStrainReductionMethod, & - ApplyBubbles, MassAssembly, HarmonicAssembly, LocalRHSForce, ShellModelArea, & - TotalErr, BenchmarkProblem=SolveBenchmarkCase) + ApplyBubbles, DrillingDOFs, DrillingPar, RotateDOFs, MassAssembly, HarmonicAssembly, & + LocalRHSForce, ShellModelArea, TotalErr, BenchmarkProblem=SolveBenchmarkCase) IF (LargeDeflection .AND. NonlinIter == 1) THEN ! --------------------------------------------------------------------------- @@ -563,8 +580,13 @@ SUBROUTINE ShellSolver(Model, Solver, dt, TransientSimulation) LocalSol = 0.0d0 END IF + IF (DrillingDOFs) THEN + CALL Warn('ShellSolver', 'Drilling DOFs does not yet support beam sections') + CYCLE + END IF + CALL BeamStiffnessMatrix(BGElement, n, nd+nb, nb, TransientSimulation, MassAssembly, & - HarmonicAssembly, LargeDeflection, LocalSol, LocalRHSForce) + HarmonicAssembly, LargeDeflection, LocalSol, LocalRHSForce, .TRUE.) IF (LargeDeflection .AND. NonlinIter == 1) THEN ! --------------------------------------------------------------------------- @@ -3492,9 +3514,11 @@ END SUBROUTINE SurfaceBasisVectors ! inaccurate results for thin shells (with low p)! !------------------------------------------------------------------------------ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & - StrainReductionMethod, MembraneStrainReductionMethod, Bubbles, MassAssembly, & - HarmonicAssembly, RHSForce, Area, Error, BenchmarkProblem) + StrainReductionMethod, MembraneStrainReductionMethod, Bubbles, & + DrillingDOFs, DrillingPar, RotateDOFs, MassAssembly, HarmonicAssembly, & + RHSForce, Area, Error, BenchmarkProblem) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: StrainEnergyDensity, ShearCorrectionFactor IMPLICIT NONE TYPE(Element_t), POINTER, INTENT(IN) :: BGElement ! An element of background mesh INTEGER, INTENT(IN) :: n ! The number of background element nodes @@ -3506,6 +3530,9 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & INTEGER, INTENT(IN) :: StrainReductionMethod ! The choice of strain reduction method INTEGER, INTENT(IN) :: MembraneStrainReductionMethod ! The choice of membrane strain reduction method LOGICAL, INTENT(IN) :: Bubbles ! To indicate that bubble functions are used + LOGICAL, INTENT(IN) :: DrillingDOFs ! Switches to drilling DOFs (limited functionality) + REAL(KIND=dp), INTENT(IN) :: DrillingPar ! A stabilization parameter for drilling DOFs + LOGICAL, INTENT(IN) :: RotateDOFs ! Use rotated DOFs (a tentative option) LOGICAL, INTENT(IN) :: MassAssembly ! To activate mass matrix integration LOGICAL, INTENT(IN) :: HarmonicAssembly ! To activate the global mass matrix updates REAL(KIND=dp), INTENT(OUT) :: RHSForce(:) ! Local RHS vector corresponding to external loads @@ -3547,7 +3574,7 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & REAL(KIND=dp) :: StrainVec(6), StressVec(6) REAL(KIND=dp) :: PrevSolVec(m*nd), PrevField(7) - REAL(KIND=dp) :: QBlock(3,3), Q(m*nd,m*nd) + REAL(KIND=dp) :: QBlock(3,3), Q(m*nd,m*nd), TMat(m*nd,m*nd), RotMat(3,3) REAL(KIND=dp) :: CMat(4,4), GMat(2,2) REAL(KIND=dp) :: A11, A22, SqrtDetA, A1, A2, B11, B22 REAL(KIND=dp) :: C111, C112, C221, C222, C211, C212 @@ -3733,6 +3760,7 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & END DO Q = 0.0d0 + TMat = 0.0d0 DO j=1,nd ! ------------------------------------------------------------------------ ! The following transformation is designed for the Lagrange element DOFs. @@ -3753,10 +3781,56 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & i0 = (j-1)*m Q(i0+1:i0+3,i0+1:i0+3) = QBlock(1:3,1:3) - Q(i0+4:i0+6,i0+4:i0+6) = QBlock(1:3,1:3) + ! + ! Optionally we can switch to rotated components theta such that + ! -Du[d] = d x theta + d. The tangent plane components are + ! then more intuitive when thinking in terms of moments. + ! + IF (RotateDOFs) THEN + ! + ! Create a matrix RotMat such that d x v = RotMat * v + ! + RotMat = 0.0d0 + RotMat(2,1) = abasis3(3) + RotMat(3,1) = -abasis3(2) + RotMat(1,2) = -abasis3(3) + RotMat(3,2) = abasis3(1) + RotMat(1,3) = abasis3(2) + RotMat(2,3) = -abasis3(1) + + DO k=1,3 + Q(i0+4,i0+3+k) = DOT_PRODUCT(RotMat(:,k), abasis1(:)) + Q(i0+5,i0+3+k) = DOT_PRODUCT(RotMat(:,k), abasis2(:)) + Q(i0+6,i0+3+k) = abasis3(k) + END DO + ELSE + Q(i0+4:i0+6,i0+4:i0+6) = QBlock(1:3,1:3) + END IF + + IF (DrillingDOFs) THEN + ! + ! TMat is a transformation matrix for expressing the components of + ! beta vector as rotated components a theta vector according to + ! the relation beta = d x theta + ! + TMat(i0+1,i0+1) = 1.0d0 + TMat(i0+2,i0+2) = 1.0d0 + TMat(i0+3,i0+3) = 1.0d0 + TMat(i0+4,i0+5) = -1.0d0 + TMat(i0+5,i0+4) = 1.0d0 + TMat(i0+6,i0+6) = 1.0d0 + ELSE + TMat(i0+1,i0+1) = 1.0d0 + TMat(i0+2,i0+2) = 1.0d0 + TMat(i0+3,i0+3) = 1.0d0 + TMat(i0+4,i0+4) = 1.0d0 + TMat(i0+5,i0+5) = 1.0d0 + TMat(i0+6,i0+6) = 1.0d0 + END IF END DO PrevSolVec(1:DOFs) = MATMUL(Q(1:DOFs,1:DOFs),PrevSolVec(1:DOFs)) + PrevSolVec(1:DOFs) = MATMUL(TMat(1:DOFs,1:DOFs),PrevSolVec(1:DOFs)) ! ------------------------------------------------------------------------ ! Finally, integrate local element matrices: @@ -3884,7 +3958,7 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & END IF ! The matrix description of the elasticity tensor: - CALL ElasticityMatrix(CMat, GMat, A1, A2, E, nu) + CALL ElasticityMatrix(CMat, GMat, A1, A2, E, nu, DrillingDOFs, DrillingPar) ! Shear correction factor: @@ -4063,14 +4137,24 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & END DO END IF - !---------------------------------------------------------------------- - ! Normal stress T^{33} via energy principle: We add a term of the type - ! e * T^{33}(e) - !---------------------------------------------------------------------- - BM(4,6:DOFs:m) = -Basis(1:nd) - BM(4,1:DOFs) = BM(4,1:DOFs) + nu/((1.0d0-nu)*A1**2) * BM(1,1:DOFs) + & - nu/((1.0d0-nu)*A2**2) * BM(2,1:DOFs) - + IF (DrillingDOFs) THEN + !---------------------------------------------------------------------- + ! Add terms which define the drilling DOFs: + !---------------------------------------------------------------------- + BM(4,6:DOFs:m) = Basis(1:nd) + DO p=1,nd + BM(4,(p-1)*m+2) = -0.5d0 * (dBasis(p,1) - 2.0d0 * C212 * Basis(p)) + BM(4,(p-1)*m+1) = 0.5d0 * (dBasis(p,2) - 2.0d0 * C211 * Basis(p)) + END DO + ELSE + !---------------------------------------------------------------------- + ! Normal stress T^{33} via energy principle: We add a term of the type + ! e * T^{33}(e) + !---------------------------------------------------------------------- + BM(4,6:DOFs:m) = -Basis(1:nd) + BM(4,1:DOFs) = BM(4,1:DOFs) + nu/((1.0d0-nu)*A1**2) * BM(1,1:DOFs) + & + nu/((1.0d0-nu)*A2**2) * BM(2,1:DOFs) + END IF StrainVec = 0.0d0 IF (LargeDeflection) THEN @@ -4347,7 +4431,7 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & !---------------------------------------------------------------------------------------- ! The part of transverse shear strains which depend linearly on the thickness coordinate: !---------------------------------------------------------------------------------------- - IF (.NOT. BenchmarkProblem) THEN + IF (.NOT. DrillingDOFs .AND. .NOT. BenchmarkProblem) THEN BS(3,6:DOFs:m) = dBasis(1:nd,1) BS(4,6:DOFs:m) = dBasis(1:nd,2) Weight = h**2/12.0d0 * Weight @@ -4422,11 +4506,12 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & DO j=1,nd Mass((i-1)*m+k,(j-1)*m+k) = Mass((i-1)*m+k,(j-1)*m+k) + & Basis(i) * Basis(j) * Weight - Mass((i-1)*m+3+k,(j-1)*m+3+k) = Mass((i-1)*m+3+k,(j-1)*m+3+k) + & - h**2/12.0d0 * Basis(i) * Basis(j) * Weight - Damp((i-1)*m+k,(j-1)*m+k) = Damp((i-1)*m+k,(j-1)*m+k) + & DampCoef * Basis(i) * Basis(j) * Weight + + IF (k > 2 .AND. DrillingDOFs) CYCLE + Mass((i-1)*m+3+k,(j-1)*m+3+k) = Mass((i-1)*m+3+k,(j-1)*m+3+k) + & + h**2/12.0d0 * Basis(i) * Basis(j) * Weight END DO END DO END DO @@ -4493,12 +4578,19 @@ SUBROUTINE ShellLocalMatrix(BGElement, n, nd, m, LocalSol, LargeDeflection, & !------------------------------------------------------- ! Transform to the global DOFs: !------------------------------------------------------- + Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(TMat(1:DOFs,1:DOFs)),MATMUL(Stiff(1:DOFs,1:DOFs),TMat(1:DOFs,1:DOFs))) Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),MATMUL(Stiff(1:DOFs,1:DOFs),Q(1:DOFs,1:DOFs))) + + Force(1:DOFs) = MATMUL(TRANSPOSE(TMat(1:DOFs,1:DOFs)),Force(1:DOFs)) Force(1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),Force(1:DOFs)) - IF (LargeDeflection) RHSForce(1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),RHSForce(1:DOFs)) + IF (LargeDeflection) THEN + ! RHSForce(1:DOFs) = MATMUL(TRANSPOSE(TMat(1:DOFs,1:DOFs)),RHSForce(1:DOFs)) + RHSForce(1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),RHSForce(1:DOFs)) + END IF IF ( MassAssembly ) THEN + Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(TMat(1:DOFs,1:DOFs)),MATMUL(Mass(1:DOFs,1:DOFs),TMat(1:DOFs,1:DOFs))) Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),MATMUL(Mass(1:DOFs,1:DOFs),Q(1:DOFs,1:DOFs))) Damp(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(Q(1:DOFs,1:DOFs)),MATMUL(Damp(1:DOFs,1:DOFs),Q(1:DOFs,1:DOFs))) @@ -5318,67 +5410,34 @@ END SUBROUTINE WriteElementNodesVariables !------------------------------------------------------------------------------ -!------------------------------------------------------------------------------ - SUBROUTINE ShearCorrectionFactor(Kappa,Thickness,x,y,n) -!------------------------------------------------------------------------------ - IMPLICIT NONE - REAL(KIND=dp) :: Kappa,Thickness,x(:),y(:) - INTEGER :: n -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: x21,x32,x43,x13,x14,y21,y32,y43,y13,y14, & - l21,l32,l43,l13,l14,alpha,h -!------------------------------------------------------------------------------ - Kappa = 1.0d0 - SELECT CASE(n) - CASE(3) - alpha = 0.20d0 - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x13 = x(1)-x(1) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y13 = y(1)-y(1) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l13 = SQRT(x13**2 + y13**2) - h = MAX(l21,l32,l13) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - CASE(4) - alpha = 0.10d0 - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x43 = x(4)-x(3) - x14 = x(1)-x(4) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y43 = y(4)-y(3) - y14 = y(1)-y(4) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l43 = SQRT(x43**2 + y43**2) - l14 = SQRT(x14**2 + y14**2) - h = MAX(l21,l32,l43,l14) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - - CASE DEFAULT - CALL Fatal('ShearCorrectionFactor',& - 'Illegal number of nodes for Smitc elements: '//TRIM(I2S(n))) - END SELECT -!------------------------------------------------------------------------------ - END SUBROUTINE ShearCorrectionFactor -!------------------------------------------------------------------------------ - - !------------------------------------------------------------------------------ ! The matrix representation of the elasticity tensor with respect an orthogonal ! basis. The case A1 = A2 = 1 corresponds to an orthonormal basis. !------------------------------------------------------------------------------ - SUBROUTINE ElasticityMatrix(CMat, GMat, A1, A2, E, nu) + SUBROUTINE ElasticityMatrix(CMat, GMat, A1, A2, E, nu, DrillingDOFs, StabPar) !------------------------------------------------------------------------------ IMPLICIT NONE REAL(KIND=dp), INTENT(OUT) :: CMat(4,4), GMat(2,2) REAL(KIND=dp), INTENT(IN) :: A1, A2, E, nu + LOGICAL, OPTIONAL, INTENT(IN) :: DrillingDOFs + REAL(KIND=dp), OPTIONAL, INTENT(IN) :: StabPar !------------------------------------------------------------------------------ + LOGICAL :: WithDrillingDOFs + REAL(KIND=dp) :: StabConst +!------------------------------------------------------------------------------ + IF (PRESENT(DrillingDOFs)) THEN + WithDrillingDOFs = DrillingDOFs + IF (WithDrillingDOFs) THEN + IF (PRESENT(StabPar)) THEN + StabConst = StabPar + ELSE + StabConst = 1.0d0 + END IF + END IF + ELSE + WithDrillingDOFs = .FALSE. + END IF + CMat = 0.0d0 GMat = 0.0d0 @@ -5395,10 +5454,14 @@ SUBROUTINE ElasticityMatrix(CMat, GMat, A1, A2, E, nu) CMat(2,2) = CMat(2,2)/A2**4 CMat(3,3) = CMat(3,3)/(A1**2 * A2**2) - ! The row corresponding to the normal stress: A deviation from the state of - ! vanishing normal stress produces deformation energy as described by - ! the 3-D Hooke's law. - CMat(4,4) = (1.0d0-nu) * E /( (1.0d0+nu) * (1.0d0-2.0d0*nu) ) + IF (WithDrillingDOFs) THEN + CMat(4,4) = StabConst * E/(1.0d0 + nu) + ELSE + ! The row corresponding to the normal stress: A deviation from the state of + ! vanishing normal stress produces deformation energy as described by + ! the 3-D Hooke's law. + CMat(4,4) = (1.0d0-nu) * E /( (1.0d0+nu) * (1.0d0-2.0d0*nu) ) + END IF GMat(1,1) = E/(2.0d0*(1.0d0 + nu)*A1**2) GMat(2,2) = E/(2.0d0*(1.0d0 + nu)*A2**2) @@ -5406,29 +5469,7 @@ SUBROUTINE ElasticityMatrix(CMat, GMat, A1, A2, E, nu) END SUBROUTINE ElasticityMatrix !------------------------------------------------------------------------------ -!------------------------------------------------------------------------------ -! Perform the operation -! -! A = A + C' * B * C * s -! -! with -! -! Size( A ) = n x n -! Size( B ) = m x m -! Size( C ) = m x n -!------------------------------------------------------------------------------ - SUBROUTINE StrainEnergyDensity(A, B, C, m, n, s) -!------------------------------------------------------------------------------ - IMPLICIT NONE - REAL(KIND=dp), INTENT(INOUT) :: A(:,:) - REAL(KIND=dp), INTENT(IN) :: B(:,:), C(:,:) - INTEGER, INTENT(IN) :: m, n - REAL(KIND=dp), INTENT(IN) :: s -!------------------------------------------------------------------------------ - A(1:n,1:n) = A(1:n,1:n) + s * MATMUL(TRANSPOSE(C(1:m,1:n)),MATMUL(B(1:m,1:m),C(1:m,1:n))) -!------------------------------------------------------------------------------ - END SUBROUTINE StrainEnergyDensity -!------------------------------------------------------------------------------ + !------------------------------------------------------------------------------ @@ -6624,445 +6665,6 @@ FUNCTION EdgeMidNode(Element, e) RESULT(X) END FUNCTION EdgeMidNode !----------------------------------------------------------------------- -!------------------------------------------------------------------------------ -! Integrate and assemble the local beam stiffness matrix. The local DOFs always -! correspond to the displacement components along the tangent direction and the -! principal axes of the cross section. The transformation to global DOFs is done -! within this subroutine. The stiffness matrix K corresponding to the global -! DOFs is thus obtained as K = R^T k R and the RHS vector F is obtained as -! F = R^T f. -! -! This routine is basically a copy of the routine contained in BeamSolver3D.F90. -! The differences are within successive delimiters " !*** ". -! TO DO: Avoid having two versions of the same routine by moving this to a single -! place. -!------------------------------------------------------------------------------ - SUBROUTINE BeamStiffnessMatrix(Element, n, nd, nb, TransientSimulation, & - MassAssembly, HarmonicAssembly, LargeDeflection, LocalSol, RHSForce) -!------------------------------------------------------------------------------ - IMPLICIT NONE - TYPE(Element_t), POINTER, INTENT(IN) :: Element - INTEGER, INTENT(IN) :: n, nd, nb - LOGICAL, INTENT(IN) :: TransientSimulation - LOGICAL, OPTIONAL, INTENT(IN) :: MassAssembly ! To activate mass matrix integration - LOGICAL, OPTIONAL, INTENT(IN) :: HarmonicAssembly ! To activate the global mass matrix updates - LOGICAL, OPTIONAL, INTENT(IN) :: LargeDeflection ! To activate nonlinear terms - REAL(KIND=dp), OPTIONAL, INTENT(IN) :: LocalSol(:,:) ! The previous solution iterate - REAL(KIND=dp), INTENT(OUT) :: RHSForce(:) ! Local RHS vector corresponding to external loads -!------------------------------------------------------------------------------ - TYPE(ValueList_t), POINTER :: BodyForce, Material - TYPE(Nodes_t) :: Nodes, LocalNodes - TYPE(GaussIntegrationPoints_t) :: IP - - LOGICAL :: Found, Stat - LOGICAL :: NonlinAssembly - - INTEGER :: DOFs - INTEGER :: i, t, p, q - INTEGER :: i0, p0, q0 - - REAL(KIND=dp), POINTER :: ArrayPtr(:,:) => NULL() - REAL(KIND=dp), POINTER :: StiffBlock(:,:), MassBlock(:,:) - REAL(KIND=dp), DIMENSION(3), PARAMETER :: ZBasis = (/ 0.0d0, 0.0d0, 0.1d1 /) - - REAL(KIND=dp), TARGET :: Mass(6*nd,6*nd), Stiff(6*nd,6*nd), Damp(6*nd,6*nd) - REAL(KIND=dp) :: Force(6*nd) - REAL(KIND=dp) :: RBlock(3,3), R(6*nd,6*nd) - REAL(KIND=dp) :: Basis(nd), dBasis(nd,3), DetJ, Weight - REAL(KIND=dp) :: Youngs_Modulus(n), Shear_Modulus(n), Area(n), Density(n) - REAL(KIND=dp) :: Torsional_Constant(n) - REAL(KIND=dp) :: Area_Moment_2(n), Area_Moment_3(n) - REAL(KIND=dp) :: Mass_Inertia_Moment(n) - REAL(KIND=dp) :: Load(3,n), f(3) - REAL(KIND=dp) :: PrevSolVec(6*nd) - REAL(KIND=dp) :: E, A, G, rho - REAL(KIND=dp) :: EA, GA, MOI, Mass_per_Length - REAL(KIND=dp) :: E_diag(3) - - REAL(KIND=dp) :: p1(3), p2(3), e1(3), e2(3), e3(3) - REAL(KIND=dp) :: L, Norm - - SAVE Nodes, LocalNodes -!------------------------------------------------------------------------------ - IF (n > 2) CALL Fatal('BeamSolver3D', & - 'Only 2-node background meshes supported currently') - - DOFs = 6 -! dim = CoordinateSystemDimension() - - CALL GetElementNodes(Nodes) - - Mass = 0.0_dp - Stiff = 0.0_dp - Damp = 0.0_dp - Force = 0.0_dp -!*** - RHSForce = 0.0d0 - IF (PRESENT(LargeDeflection)) THEN - NonlinAssembly = LargeDeflection - ELSE - NonlinAssembly = .FALSE. - END IF - IF (NonlinAssembly) THEN - IF (.NOT. PRESENT(LocalSol)) CALL Fatal('BeamStiffnessMatrix', & - 'Previous solution iterate needed') - DO i=1,DOFs - PrevSolVec(i:DOFs*(nd-nb):DOFs) = LocalSol(i,1:(nd-nb)) - END DO - END IF -!*** - - BodyForce => GetBodyForce() - IF ( ASSOCIATED(BodyForce) ) THEN - ! - ! Force components refer to the basis of the global frame: - ! - Load(1,1:n) = GetReal(BodyForce, 'Body Force 1', Found) - Load(2,1:n) = GetReal(BodyForce, 'Body Force 2', Found) - Load(3,1:n) = GetReal(BodyForce, 'Body Force 3', Found) - ELSE - Load = 0.0_dp - END IF - - Material => GetMaterial() - Youngs_Modulus(1:n) = GetReal(Material, 'Youngs Modulus', Found) - Shear_Modulus(1:n) = GetReal(Material, 'Shear Modulus', Found) - Area(1:n) = GetReal(Material, 'Cross Section Area', Found) - Torsional_Constant(1:n) = GetReal(Material, 'Torsional Constant', Found) - Area_Moment_2(1:n) = GetReal(Material, 'Second Moment of Area 2', Found) - Area_Moment_3(1:n) = GetReal(Material, 'Second Moment of Area 3', Found) - -!*** IF (TransientSimulation) THEN - IF (MassAssembly) THEN - Density(1:n) = GetReal(Material, 'Density', Found) - END IF - - ! - ! Compute the tangent vector e1 to the beam axis: - ! - p1(1) = Nodes % x(1) - p1(2) = Nodes % y(1) - p1(3) = Nodes % z(1) - p2(1) = Nodes % x(2) - p2(2) = Nodes % y(2) - p2(3) = Nodes % z(2) - e1 = p2 - p1 - L = SQRT(SUM(e1(:)**2)) - e1 = 1.0_dp/L * e1 - ! - ! Cross section parameters are given with respect to a local frame. - ! Determine its orientation: - ! -!*** - ArrayPtr => ListGetConstRealArray(Material, 'Director', Found) - IF (Found) THEN - e3 = 0.0d0 - DO i=1,SIZE(ArrayPtr,1) - e3(i) = ArrayPtr(i,1) - END DO - Norm = SQRT(SUM(e3(:)**2)) - e3 = 1.0_dp/Norm * e3 - IF (ABS(DOT_PRODUCT(e1,e3)) > 100.0_dp * AEPS) CALL Fatal('BeamSolver3D', & - 'Director should be orthogonal to the beam axis') - e2 = CrossProduct(e3, e1) -!*** - ELSE - ArrayPtr => ListGetConstRealArray(Material, 'Principal Direction 2', Found) - IF (Found) THEN - e2 = 0.0d0 - DO i=1,SIZE(ArrayPtr,1) - e2(i) = ArrayPtr(i,1) - END DO - Norm = SQRT(SUM(e2(:)**2)) - e2 = 1.0_dp/Norm * e2 - ELSE - e2 = -ZBasis - END IF - IF (ABS(DOT_PRODUCT(e1,e2)) > 100.0_dp * AEPS) CALL Fatal('BeamSolver3D', & - 'Principal Direction 2 should be orthogonal to the beam axis') - e3 = CrossProduct(e1, e2) - END IF - - - ! - ! Allocate an additional variable so as to write nodes data with respect to - ! the local frame. - ! - IF (.NOT. ASSOCIATED(LocalNodes % x)) THEN - ALLOCATE(LocalNodes % x(n), LocalNodes % y(n), LocalNodes % z(n) ) - LocalNodes % NumberOfNodes = n - LocalNodes % y(:) = 0.0_dp - LocalNodes % z(:) = 0.0_dp - END IF - LocalNodes % x(1) = 0.0d0 - LocalNodes % x(2) = L - - !----------------------- - ! Numerical integration: - !----------------------- -!*** - IF (.NOT. IsPElement(Element) .AND. nd > n) THEN - IP = GaussPoints(Element, 3) - ELSE - IP = GaussPoints(Element) - END IF -!*** - DO t=1,IP % n - !-------------------------------------------------------------- - ! Basis function values & derivatives at the integration point: - !-------------------------------------------------------------- - stat = ElementInfo(Element, LocalNodes, IP % U(t), IP % V(t), & - IP % W(t), detJ, Basis, dBasis) - -!*** - ! Create a bubble if the element is the standard 2-node element: - IF (.NOT. IsPElement(Element) .AND. nd > n) THEN - Basis(n+1) = Basis(1) * Basis(2) - dBasis(3,:) = dBasis(1,:) * Basis(2) + Basis(1) * dBasis(2,:) - END IF -!*** - !------------------------------------------ - ! The model data at the integration point: - !------------------------------------------ - f(1) = SUM(Basis(1:n) * Load(1,1:n)) - f(2) = SUM(Basis(1:n) * Load(2,1:n)) - f(3) = SUM(Basis(1:n) * Load(3,1:n)) - - ! TO DO: Add option to give the applied moment load - - E = SUM(Basis(1:n) * Youngs_Modulus(1:n)) - G = SUM(Basis(1:n) * Shear_Modulus(1:n)) - A = SUM(Basis(1:n) * Area(1:n)) - - E_diag(1) = G * SUM(Basis(1:n) * Torsional_Constant(1:n)) - E_diag(2) = E * SUM(Basis(1:n) * Area_Moment_2(1:n)) - E_diag(3) = E * SUM(Basis(1:n) * Area_Moment_3(1:n)) - -!*** IF (TransientSimulation) THEN - IF (MassAssembly) THEN - rho = SUM(Basis(1:n) * Density(1:n)) - MOI = rho/E * sqrt(E_diag(2)**2 + E_diag(3)**2) - Mass_per_Length = rho * A - END IF - - GA = G*A - EA = E*A - - ! TO DO: Add option to give shear correction factors - - Weight = IP % s(t) * DetJ - - DO p=1,nd - p0 = (p-1)*DOFs - DO q=1,nd - q0 = (q-1)*DOFs - StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) - MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) - ! - ! (Du',v'): - ! - StiffBlock(1,1) = StiffBlock(1,1) + & - EA * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(2,2) = StiffBlock(2,2) + & - GA * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(3,3) = StiffBlock(3,3) + & - GA * dBasis(q,1) * dBasis(p,1) * Weight - -!*** IF (TransientSimulation) THEN - IF (MassAssembly) THEN - MassBlock(1,1) = MassBlock(1,1) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - MassBlock(2,2) = MassBlock(2,2) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - MassBlock(3,3) = MassBlock(3,3) + & - Mass_per_Length * Basis(q) * Basis(p) * Weight - END IF - - IF (q > n) CYCLE - ! - ! -(D theta x t,v'): - ! - StiffBlock(2,6) = StiffBlock(2,6) - & - GA * Basis(q) * dBasis(p,1) * Weight - StiffBlock(3,5) = StiffBlock(3,5) + & - GA * Basis(q) * dBasis(p,1) * Weight - END DO - - Force(p0+1) = Force(p0+1) + Weight * DOT_PRODUCT(f,e1)* Basis(p) - Force(p0+2) = Force(p0+2) + Weight * DOT_PRODUCT(f,e2)* Basis(p) - Force(p0+3) = Force(p0+3) + Weight * DOT_PRODUCT(f,e3)* Basis(p) - - IF (p > n) CYCLE - - DO q=1,nd - q0 = (q-1)*DOFs - StiffBlock => Stiff(p0+1:p0+DOFs,q0+1:q0+DOFs) - MassBlock => Mass(p0+1:p0+DOFs,q0+1:q0+DOFs) - ! - ! -(D u',psi x t): - ! - StiffBlock(5,3) = StiffBlock(5,3) + & - GA * Basis(p) * dBasis(q,1) * Weight - StiffBlock(6,2) = StiffBlock(6,2) - & - GA * Basis(p) * dBasis(q,1) * Weight - - IF (q > n) CYCLE - - ! - ! (E theta',psi') + (D theta x t,psi x t): - ! - StiffBlock(4,4) = StiffBlock(4,4) + & - E_diag(1) * dBasis(q,1) * dBasis(p,1) * Weight - StiffBlock(5,5) = StiffBlock(5,5) + & - E_diag(2) * dBasis(q,1) * dBasis(p,1) * Weight + & - GA * Basis(p) * Basis(q) * Weight - StiffBlock(6,6) = StiffBlock(6,6) + & - E_diag(3) * dBasis(q,1) * dBasis(p,1) * Weight + & - GA * Basis(p) * Basis(q) * Weight - -!*** IF (TransientSimulation) THEN - IF (MassAssembly) THEN - MassBlock(4,4) = MassBlock(4,4) + MOI * Basis(q) * Basis(p) * Weight - MassBlock(5,5) = MassBlock(5,5) + rho/E * E_diag(2) * & - Basis(q) * Basis(p) * Weight - MassBlock(6,6) = MassBlock(6,6) + rho/E * E_diag(3) * & - Basis(q) * Basis(p) * Weight - END IF - - END DO - END DO - END DO - - CALL BeamCondensate(nd-nb, nb, DOFs, 3, Stiff, Force) - -!*** - ! - ! Switch to rotation variables which conform with the rotated moments - M x d: - ! - R = 0.0d0 - DO i=1,nd-nb - i0 = (i-1)*DOFs - R(i0+1,i0+1) = 1.0d0 - R(i0+2,i0+2) = 1.0d0 - R(i0+3,i0+3) = 1.0d0 - R(i0+4,i0+5) = 1.0d0 - R(i0+5,i0+4) = -1.0d0 - R(i0+6,i0+6) = 1.0d0 - END DO - DOFs = (nd-nb)*DOFs - Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Stiff(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - Force(1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)),Force(1:DOFs)) - - IF (MassAssembly) & - Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Mass(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - - ! - ! The moment around the director is not compatible with the shell model. - ! Remove its contribution: - ! - DO p=1,nd-nb - Stiff(6*p,:) = 0.0d0 - Stiff(:,6*p) = 0.0d0 - Stiff(6*p,6*p) = 0.0d0 - Force(6*p) = 0.0d0 - Mass(6*p,:) = 0.0d0 - Mass(:,6*p) = 0.0d0 - END DO -!*** - - ! - ! Build the transformation matrix in order to switch to the global DOFs - ! - DOFs = 6 - R = 0.0d0 - RBlock(1,1:3) = e1(1:3) - RBlock(2,1:3) = e2(1:3) - RBlock(3,1:3) = e3(1:3) - DO i=1,nd-nb - i0 = (i-1)*DOFs - R(i0+1:i0+3,i0+1:i0+3) = RBlock(1:3,1:3) - R(i0+4:i0+6,i0+4:i0+6) = RBlock(1:3,1:3) - END DO - - !------------------------------------------------------- - ! Transform to the global DOFs: - !------------------------------------------------------- - DOFs = (nd-nb)*DOFs - Stiff(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Stiff(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - Force(1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)),Force(1:DOFs)) - -!*** - RHSForce(1:DOFs) = Force(1:DOFs) - IF (NonlinAssembly) Force(1:DOFs) = Force(1:DOFs) - & - MATMUL(Stiff(1:DOFs,1:DOFs), PrevSolVec(1:DOFs)) -!*** - - IF (MassAssembly) THEN - Mass(1:DOFs,1:DOFs) = MATMUL(TRANSPOSE(R(1:DOFs,1:DOFs)), & - MATMUL(Mass(1:DOFs,1:DOFs),R(1:DOFs,1:DOFs))) - IF (TransientSimulation) THEN - CALL Default2ndOrderTime(Mass, Damp, Stiff, Force) - ELSE IF (HarmonicAssembly) THEN - CALL DefaultUpdateMass(Mass) - END IF - END IF - - CALL DefaultUpdateEquations(Stiff, Force) -!------------------------------------------------------------------------------ - END SUBROUTINE BeamStiffnessMatrix -!------------------------------------------------------------------------------ - -!------------------------------------------------------------------------------ - SUBROUTINE BeamCondensate(n, nb, dofs, dim, K, F, F1 ) -!------------------------------------------------------------------------------ - USE LinearAlgebra - IMPLICIT NONE - INTEGER, INTENT(IN) :: n ! Nodes after condensation - INTEGER, INTENT(IN) :: nb ! The number of bubble basis functions - INTEGER, INTENT(IN) :: dofs ! DOFs per node - INTEGER, INTENT(IN) :: dim ! The first dim fields have bubbles - REAL(KIND=dp), INTENT(INOUT) :: K(:,:) ! The stiffness matrix - REAL(KIND=dp), INTENT(INOUT) :: F(:) ! The RHS vector - REAL(KIND=dp), OPTIONAL, INTENT(INOUT) :: F1(:) ! Some other RHS vector -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Kbl(nb*dim,n*dofs), Kbb(nb*dim,nb*dim), Fb(nb*dim) - REAL(KIND=dp) :: Klb(n*dofs,nb*dim) - - INTEGER :: i, m, p, Cdofs(dofs*n), Bdofs(dim*nb) -!------------------------------------------------------------------------------ - - Cdofs(1:n*dofs) = (/ (i, i=1,n*dofs) /) - - m = 0 - DO p = 1,nb - DO i = 1,dim - m = m + 1 - Bdofs(m) = dofs*(n+p-1) + i - END DO - END DO - - Kbb = K(Bdofs,Bdofs) - Kbl = K(Bdofs,Cdofs) - Klb = K(Cdofs,Bdofs) - Fb = F(Bdofs) - - CALL InvertMatrix( Kbb,nb*dim ) - - F(1:dofs*n) = F(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) - K(1:dofs*n,1:dofs*n) = & - K(1:dofs*n,1:dofs*n) - MATMUL( Klb, MATMUL( Kbb,Kbl ) ) - - IF (PRESENT(F1)) THEN - Fb = F1(Bdofs) - F1(1:dofs*n) = F1(1:dofs*n) - MATMUL( Klb, MATMUL( Kbb, Fb ) ) - END IF -!------------------------------------------------------------------------------ - END SUBROUTINE BeamCondensate -!------------------------------------------------------------------------------ - !------------------------------------------------------------------------------ END SUBROUTINE ShellSolver !------------------------------------------------------------------------------ diff --git a/fem/src/modules/Smitc.F90 b/fem/src/modules/Smitc.F90 index 31603f138f..f7be747ca4 100755 --- a/fem/src/modules/Smitc.F90 +++ b/fem/src/modules/Smitc.F90 @@ -289,8 +289,11 @@ SUBROUTINE SmitcSolver( Model,Solver,dt,TransientSimulation ) !------------------------------------------------------------------------------ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & - Force, Load, Element, n, DOFs, Nodes, DampingCoef, SpringCoef ) + Force, Load, Element, n, DOFs, Nodes, DampingCoef, SpringCoef ) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: StrainEnergyDensity, ShearCorrectionFactor, & + IsotropicElasticity + REAL(KIND=dp) :: STIFF(:,:), DAMP(:,:), & MASS(:,:), Force(:), Load(:), DampingCoef(:), SpringCoef(:) INTEGER :: n, DOFs @@ -369,7 +372,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & Curvature(3,3*p ) = dBasisdx(p,1) END DO - CALL AddInnerProducts(STIFF,Ematrix,Curvature,3,3*n,s) + CALL StrainEnergyDensity(STIFF,Ematrix,Curvature,3,3*n,s) ! In-plane stiffness: ! ------------------- @@ -388,7 +391,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & ShearStrain(2,3*p-2) = dBasisdx(p,2) END DO - CALL AddInnerProducts(STIFF, & + CALL StrainEnergyDensity(STIFF, & Gmatrix,ShearStrain,2,3*n,Kappa*s) ! ! Tensile stiffness: @@ -399,7 +402,7 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & ShearStrain(2,3*p-2) = dBasisdx(p,2) END DO - CALL AddInnerProducts(STIFF,Tmatrix,ShearStrain,2,3*n,s) + CALL StrainEnergyDensity(STIFF,Tmatrix,ShearStrain,2,3*n,s) ! Spring Coeffficient: ! ------------------- @@ -445,38 +448,8 @@ SUBROUTINE LocalMatrix( STIFF, DAMP, MASS, & END DO !------------------------------------------------------------------------------ END SUBROUTINE LocalMatrix - - -!============================================================================== - - - SUBROUTINE IsotropicElasticity(Ematrix, & - Gmatrix,Poisson,Young,Thickness,Basis,n) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Ematrix(:,:), Gmatrix(:,:), Basis(:) - REAL(KIND=dp) :: Poisson(:), Young(:), Thickness(:) - REAL(KIND=dp) :: Euvw, Puvw, Guvw, Tuvw - INTEGER :: n !------------------------------------------------------------------------------ - Euvw = SUM( Young(1:n)*Basis(1:n) ) - Puvw = SUM( Poisson(1:n)*Basis(1:n) ) - Tuvw = SUM( Thickness(1:n)*Basis(1:n) ) - Guvw = Euvw/(2.0d0*(1.0d0 + Puvw)) - - Ematrix = 0.0d0 - Ematrix(1,1) = 1.0d0 - Ematrix(1,2) = Puvw - Ematrix(2,1) = Puvw - Ematrix(2,2) = 1.0d0 - Ematrix(3,3) = (1.0d0-Puvw)/2.0d0 - - Ematrix = Ematrix* Euvw * (Tuvw**3) / (12.0d0*(1.0d0-Puvw**2)) - Gmatrix = 0.0d0 - Gmatrix(1,1) = Guvw*Tuvw - Gmatrix(2,2) = Guvw*Tuvw -!------------------------------------------------------------------------------ - END SUBROUTINE IsotropicElasticity !------------------------------------------------------------------------------ @@ -486,7 +459,6 @@ END SUBROUTINE IsotropicElasticity !> in silicon condenser microphones', Sensors and Actuators A 54 (1996) 499-504. ! The model in verified in the special assignment of Jani Paavilainen !------------------------------------------------------------------------------ - SUBROUTINE PerforatedElasticity(Ematrix, & Gmatrix,Poisson,Young,Thickness,HoleFraction, & HoleSize, Basis,n) @@ -529,94 +501,13 @@ SUBROUTINE PerforatedElasticity(Ematrix, & Gmatrix(2,2) = Gmatrix(1,1) !------------------------------------------------------------------------------ END SUBROUTINE PerforatedElasticity - -!============================================================================== - - - SUBROUTINE ShearCorrectionFactor(Kappa,Thickness,x,y,n) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Kappa,Thickness,x(:),y(:) - INTEGER :: n -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: x21,x32,x43,x13,x14,y21,y32,y43,y13,y14, & - l21,l32,l43,l13,l14,alpha,h -!------------------------------------------------------------------------------ - Kappa = 1.0d0 - SELECT CASE(n) - CASE(3) - alpha = 0.20d0 - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x13 = x(1)-x(1) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y13 = y(1)-y(1) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l13 = SQRT(x13**2 + y13**2) - h = MAX(l21,l32,l13) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - CASE(4) - alpha = 0.10d0 - x21 = x(2)-x(1) - x32 = x(3)-x(2) - x43 = x(4)-x(3) - x14 = x(1)-x(4) - y21 = y(2)-y(1) - y32 = y(3)-y(2) - y43 = y(4)-y(3) - y14 = y(1)-y(4) - l21 = SQRT(x21**2 + y21**2) - l32 = SQRT(x32**2 + y32**2) - l43 = SQRT(x43**2 + y43**2) - l14 = SQRT(x14**2 + y14**2) - h = MAX(l21,l32,l43,l14) - Kappa = (Thickness**2)/(Thickness**2 + alpha*(h**2)) - CASE DEFAULT - CALL Fatal('SmitcSolver','Illegal number of nodes for Smitc elements: '//TRIM(I2S(n))) - END SELECT -!------------------------------------------------------------------------------ - END SUBROUTINE ShearCorrectionFactor - - -!============================================================================== - - - SUBROUTINE AddInnerProducts(A,B,C,m,n,s) -!------------------------------------------------------------------------------ -! Performs the operation -! -! A = A + C' * B * C * s -! -! with -! -! Size( A ) = n x n -! Size( B ) = m x m -! Size( C ) = m x n -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: A(:,:),B(:,:),C(:,:),s - INTEGER :: m,n -!------------------------------------------------------------------------------ - INTEGER :: i,j,k,l -!------------------------------------------------------------------------------ - DO i=1,n - DO j=1,n - DO k=1,m - DO l=1,m - A(i,j) = A(i,j) + C(k,i)*B(k,l)*C(l,j) * s - END DO - END DO - END DO - END DO !------------------------------------------------------------------------------ - END SUBROUTINE AddInnerProducts - - -!============================================================================== +!------------------------------------------------------------------------------ SUBROUTINE CovariantInterpolation(ShearStrain,Basis,X,Y,U,V,n) !------------------------------------------------------------------------------ + USE SolidMechanicsUtils, ONLY: Jacobi3, Jacobi4 REAL(KIND=dp) :: ShearStrain(:,:),Basis(:),X(:),Y(:),U,V INTEGER :: n !------------------------------------------------------------------------------ @@ -772,68 +663,9 @@ SUBROUTINE CovariantInterpolation(ShearStrain,Basis,X,Y,U,V,n) END SELECT !------------------------------------------------------------------------------ END SUBROUTINE CovariantInterpolation - - -!============================================================================== - - - SUBROUTINE Jacobi3(Jmat,invJ,detJ,x,y) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Jmat(:,:),invJ(:,:),detJ,x(:),y(:) -!------------------------------------------------------------------------------ - Jmat(1,1) = x(2)-x(1) - Jmat(2,1) = x(3)-x(1) - Jmat(1,2) = y(2)-y(1) - Jmat(2,2) = y(3)-y(1) - - detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) - - invJ(1,1) = Jmat(2,2)/detJ - invJ(2,2) = Jmat(1,1)/detJ - invJ(1,2) = -Jmat(1,2)/detJ - invJ(2,1) = -Jmat(2,1)/detJ !------------------------------------------------------------------------------ - END SUBROUTINE Jacobi3 - -!============================================================================== - - SUBROUTINE Jacobi4(Jmat,invJ,detJ,xi,eta,x,y) -!------------------------------------------------------------------------------ - REAL(KIND=dp) :: Jmat(:,:),invJ(:,:),detJ,xi,eta,x(:),y(:) !------------------------------------------------------------------------------ - REAL(KIND=dp) :: dNdxi(4), dNdeta(4) - INTEGER :: i - - dNdxi(1) = -(1-eta)/4.0d0 - dNdxi(2) = (1-eta)/4.0d0 - dNdxi(3) = (1+eta)/4.0d0 - dNdxi(4) = -(1+eta)/4.0d0 - dNdeta(1) = -(1-xi)/4.0d0 - dNdeta(2) = -(1+xi)/4.0d0 - dNdeta(3) = (1+xi)/4.0d0 - dNdeta(4) = (1-xi)/4.0d0 - - Jmat = 0.0d0 - DO i=1,4 - Jmat(1,1) = Jmat(1,1) + dNdxi(i)*x(i) - Jmat(1,2) = Jmat(1,2) + dNdxi(i)*y(i) - Jmat(2,1) = Jmat(2,1) + dNdeta(i)*x(i) - Jmat(2,2) = Jmat(2,2) + dNdeta(i)*y(i) - END DO - - detJ = Jmat(1,1)*Jmat(2,2)-Jmat(1,2)*Jmat(2,1) - - invJ(1,1) = Jmat(2,2)/detJ - invJ(2,2) = Jmat(1,1)/detJ - invJ(1,2) = -Jmat(1,2)/detJ - invJ(2,1) = -Jmat(2,1)/detJ -!------------------------------------------------------------------------------ - END SUBROUTINE Jacobi4 - -!============================================================================== - - END SUBROUTINE SmitcSolver !------------------------------------------------------------------------------ diff --git a/fem/src/modules/VectorHelmholtz.F90 b/fem/src/modules/VectorHelmholtz.F90 index c56f6d76b9..684f6a2c4a 100755 --- a/fem/src/modules/VectorHelmholtz.F90 +++ b/fem/src/modules/VectorHelmholtz.F90 @@ -542,7 +542,7 @@ SUBROUTINE LocalMatrix( Element, n, nd, InitHandles ) DO i = 1,nd DO j = 1,nd ! the term i\omega\sigma E.v - STIFF(i,j) = STIFF(i,j) + im * Omega * Cond * & + STIFF(i,j) = STIFF(i,j) - im * Omega * Cond * & SUM(WBasis(j,:) * WBasis(i,:)) * weight END DO END DO diff --git a/fem/src/view3d/TestModel.c b/fem/src/view3d/TestModel.c index dfb4531c80..c4961a12a8 100755 --- a/fem/src/view3d/TestModel.c +++ b/fem/src/view3d/TestModel.c @@ -280,7 +280,8 @@ fprintf( stderr, "%d\n", 5 ); void MakeTestModelLinear() { double a,r,PI=2*acos(0.0),XMin,XMax,YMin,YMax,ZMin,ZMax,*nx,*ny,*nz; - + static char *ioptr; + int i,j,k,n,NN,NE; FILE *fp = fopen( "qq.qq", "r" ); @@ -301,12 +302,12 @@ void MakeTestModelLinear() ShapeFunctionMatrix3[2][1] = 0.0; ShapeFunctionMatrix3[2][2] = 1.0; - fgets( str,100,fp ); + ioptr = fgets( str,100,fp ); sscanf( str, "%d %d", &NN, &NE ); for( j=0; j GetBC() + tn = GetTimestep() + + IF( tn /= tn0 ) THEN + tn0 = tn + + Name = "Timeprofile" + + ptr => ListFind(BC,Name,Found) + IF(.NOT. Found ) CALL Fatal('TimeProfile','Could not find item: '//TRIM(Name)) + + IF( ptr % TYPE /= LIST_TYPE_VARIABLE_SCALAR ) THEN + CALL Fatal('TimeProfile','Item should be variable scalar: '//TRIM(Name)) + END IF + + IF ( ptr % PROCEDURE /= 0 ) THEN + CALL Fatal('TimeProfile','Item should not be a function: '//TRIM(Name)) + END IF + + m = SIZE( ptr % Fvalues(1,1,:) ) + + DO i=1,m + f = ListGetCReal(BC,TRIM(Name)//' point '//TRIM(I2S(i)),UnfoundFatal=.TRUE.) + ptr % Fvalues(1,1,i) = f + END DO + + PRINT *,'Updated temperature profile: ',ptr % Fvalues(1,1,:) + END IF + + f = InterpolateCurve( ptr % TValues,ptr % FValues(1,1,:), & + tx, ptr % CubicCoeff ) + +END FUNCTION TimeProfile + diff --git a/fem/tests/TimeProfileBC/case.sif b/fem/tests/TimeProfileBC/case.sif new file mode 100755 index 0000000000..b1b2d6c802 --- /dev/null +++ b/fem/tests/TimeProfileBC/case.sif @@ -0,0 +1,128 @@ +! An linear profile changing with time. +! +! P.R. 6.11.2020 + +Header + CHECK KEYWORDS Warn + Mesh DB "." "slab" + Include Path "" + Results Directory "" +End + +Simulation + Max Output Level = 5 + + Coordinate System = "Cartesian" + Coordinate Mapping(3) = 1 2 3 + + Simulation Type = "Transient" + +! Tavels back and forth one time in 1s + Timestep Sizes = 1 + Timestep Intervals = 20 + + Steady State Max Iterations = 1 + Output Intervals = 1 + + Post File = case.vtu +End + +Constants + Gravity(4) = 0 -1 0 9.82 + Stefan Boltzmann = 5.67e-08 +End + +Body 1 + Name = "Body" + Equation = 1 + Material = 1 +End + +Equation 1 + Name = "Equations" + Active Solvers(2) = 1 2 +End + +Solver 1 + Exec Solver = "Always" + Equation = "Heat Equation" + Variable = "Temperature" + + Linear System Solver = direct + Linear System Direct Method = "umfpack" + + Steady State Convergence Tolerance = 1.0e-05 + + Nonlinear System Convergence Tolerance = 1.0e-05 + Nonlinear System Max Iterations = 1 +End + + +Solver 2 + Exec Solver = never + + Equation = SaveLine + Procedure = "SaveData" "SaveLine" + + Filename = f.dat +End + +Material 1 + Name = "Material" + Density = 1 + Heat Conductivity = 1.0 + Heat Capacity = 10.0 +End + + +Boundary Condition 1 + Name = "Bottom" + Target Boundaries(1) = 1 +End + +Boundary Condition 2 + Name = "Right" + Target Boundaries(1) = 2 + Temperature = 0 +End + +Boundary Condition 3 + Name = "Top" + Target Boundaries(1) = 3 + + External Temperature = Variable "coordinate 1" + Procedure "TimeProfile" "TimeProfile" + + Timeprofile = Variable "dummy" + Real + 0.0 0.0 + 0.2 0.0 + 0.4 0.0 + 0.6 0.0 + 1.0 0.0 + End + + Timeprofile point 1 = Variable "time" + Real MATC "1.0/(1.0+tx)" + Timeprofile point 2 = Variable "time" + Real MATC "1.0/(2.0+tx)" + Timeprofile point 3 = Variable "time" + Real MATC "sin(tx)" + Timeprofile point 4 = Variable "time" + Real MATC "-1.0/(1.0+2*tx)" + Timeprofile point 5 = Variable "time" + Real MATC "-1.0/(1.0+3*tx)" + + + Heat Transfer Coefficient = 1.0 + + Save Line = Logical True +End + +Boundary Condition 4 + Name = "Left" + Target Boundaries(1) = 4 + Temperature = 0 +End + +Solver 1 :: Reference Norm = 7.64764652E-02 diff --git a/fem/tests/TimeProfileBC/runtest.cmake b/fem/tests/TimeProfileBC/runtest.cmake new file mode 100755 index 0000000000..9a30ca2d8c --- /dev/null +++ b/fem/tests/TimeProfileBC/runtest.cmake @@ -0,0 +1,3 @@ +include(test_macros) +execute_process(COMMAND ${ELMERGRID_BIN} 1 2 slab) +RUN_ELMER_TEST() diff --git a/fem/tests/TimeProfileBC/slab.grd b/fem/tests/TimeProfileBC/slab.grd new file mode 100755 index 0000000000..ddea43b8c0 --- /dev/null +++ b/fem/tests/TimeProfileBC/slab.grd @@ -0,0 +1,24 @@ +***** ElmerGrid input file for structured grid generation ***** +Version = 210903 +Coordinate System = Cartesian 2D +Subcell Divisions in 2D = 1 1 +Subcell Limits 1 = 0 1 +Subcell Limits 2 = 0 0.1 +Material Structure in 2D + 1 +End +Materials Interval = 1 1 +Boundary Definitions +! type out int + 1 -1 1 1 + 2 -2 1 1 + 3 -3 1 1 + 4 -4 1 1 +End +Element Degree = 1 +Element Innernodes = False +Triangles = False +Element Divisions 1 = 100 +Element Divisions 2 = 10 +Element Ratios 1 = 1.0 +Element Ratios 2 = 1.0 diff --git a/fem/tests/VectorHelmholtzWaveguide/waveguide.sif b/fem/tests/VectorHelmholtzWaveguide/waveguide.sif index dfc7c9cc5c..ffc14279be 100755 --- a/fem/tests/VectorHelmholtzWaveguide/waveguide.sif +++ b/fem/tests/VectorHelmholtzWaveguide/waveguide.sif @@ -47,8 +47,6 @@ Material 1 Relative Permittivity = Real 1 !Relative Permittivity im = Real 0 - !Inverse Relative Permeability = Real 1 - !Inverse Relative Permeability im = Variable coordinate 3 !Real MATC "if (tx>0.1) -2.2135; else 0;" !Relative Permittivity im = Variable coordinate 3 !Real MATC "if (tx>0.1) 2.2135/5; else 0;" diff --git a/fem/tests/mgdyn2D_pm/ptest.sif b/fem/tests/mgdyn2D_pm/ptest.sif index d97a03676f..efcaf2d653 100644 --- a/fem/tests/mgdyn2D_pm/ptest.sif +++ b/fem/tests/mgdyn2D_pm/ptest.sif @@ -3,13 +3,13 @@ Check Keywords "Warn" Header:: Mesh DB "." "square" Simulation - Max Output Level = 5 + Max Output Level = 7 Coordinate System = Cartesian Simulation Type = Steady Steady State Max Iterations = 1 -! Post File = "ptest.ep" +! Post File = "ptest.vtu" End Body 1 @@ -68,15 +68,18 @@ End ! magnetic flux density Solver 3 - Equation = ComputeFlux + Equation = MgDynCalcFields - Procedure = "MagnetoDynamics2D" "bSolver" - Variable = -nooutput temp - Target Variable="A" - Exported Variable 1 = B[B:2] +! Procedure = "MagnetoDynamics2D" "bSolver" + Procedure = "MagnetoDynamics" "MagnetoDynamicsCalcFields" + + Potential Variable="A" Linear System Solver = "Direct" Linear System Direct Method = UMFPack + + Skip Compute Steady State Change = Logical True + Show Norm Index = 2 End ! make outer boundary circular + asymptotic bc @@ -98,4 +101,3 @@ Boundary Condition 2 End Solver 3 :: Reference Norm = Real 0.11769191 -RUN diff --git a/fem/tests/mgdyn_anisotropic_rel/cyl-case.sif b/fem/tests/mgdyn_anisotropic_rel/cyl-case.sif index 5db16f7413..005fdbd26b 100644 --- a/fem/tests/mgdyn_anisotropic_rel/cyl-case.sif +++ b/fem/tests/mgdyn_anisotropic_rel/cyl-case.sif @@ -171,5 +171,5 @@ Equation 1 active solvers(4) = 1 2 3 4 End -solver 3::Reference norm = 11.516506516399119 +solver 3::Reference norm = 9.54680124E+00 solver 3::Reference norm tolerance = 1e-5 diff --git a/fem/tests/mgdyn_bh/case.sif b/fem/tests/mgdyn_bh/case.sif index eaa7e9f68e..d742ac508d 100644 --- a/fem/tests/mgdyn_bh/case.sif +++ b/fem/tests/mgdyn_bh/case.sif @@ -68,6 +68,7 @@ Solver 3 Calculate Magnetic Vector Potential = False Calculate Magnetic Flux Density = True Calculate Magnetic Field Strength = Logical True + Separate Magnetic Energy = True Steady State Convergence Tolerance = 0 Linear System Solver = "Iterative" Linear System Preconditioning = None @@ -89,8 +90,8 @@ End Solver 5 Equation = "scalars" procedure = "SaveData" "SaveScalars" - !variable 1 = res: magnetic field energy - show norm index = integer 2 + show norm index = integer 3 +! Filename = "tmp.dat" End diff --git a/fem/tests/mgdyn_harmonic/MGDynamicsHarmonic.sif b/fem/tests/mgdyn_harmonic/MGDynamicsHarmonic.sif index 45907c6f42..635066451f 100755 --- a/fem/tests/mgdyn_harmonic/MGDynamicsHarmonic.sif +++ b/fem/tests/mgdyn_harmonic/MGDynamicsHarmonic.sif @@ -26,7 +26,26 @@ End Material 1 - Reluctivity = Real 1 +! Reluctivity = Real 1 +! Reluctivity Im = Real 0 + + ! + ! Here the reluctivity can be described by using a scalar parameter but + ! the following commands define it as a higher-order tensor to test + ! the functionality. Note that the postprocessing subroutine + ! MagnetoDynamicsCalcFields cannot yet handle the given magnetic anisotropy + ! correctly in the calculation of the Maxwell stress tensor. + ! + Reluctivity(3,3) = 1.0 0.0 0.0 \ + 0.0 1.0 0.0 \ + 0.0 0.0 1.0 + Reluctivity Im(3,3) = 0.0 0.0 0.0 \ + 0.0 0.0 0.0 \ + 0.0 0.0 0.0 +! +! Reluctivity(3) = 1.0 1.0 1.0 +! Reluctivity Im(3) = 0.0 0.0 0.0 + ! Permittivity = Real 1 Electric Conductivity = Real 1 End diff --git a/fem/tests/mgdyn_lamstack_lowfreq_transient/lamstack.sif b/fem/tests/mgdyn_lamstack_lowfreq_transient/lamstack.sif index 1e33afd7c9..a2157d6000 100644 --- a/fem/tests/mgdyn_lamstack_lowfreq_transient/lamstack.sif +++ b/fem/tests/mgdyn_lamstack_lowfreq_transient/lamstack.sif @@ -8,7 +8,7 @@ End $ f = 500 $ omega = 2*pi*f $ intervals = 5 -$ periods = 0.5 +$ periods = 0.5000001 $ tsize = periods/(f*intervals) Simulation diff --git a/fem/tests/mgdyn_steady_piolaversion/ZCurrent.sif b/fem/tests/mgdyn_steady_piolaversion/ZCurrent.sif index 5fc7dd8939..b959fa937a 100644 --- a/fem/tests/mgdyn_steady_piolaversion/ZCurrent.sif +++ b/fem/tests/mgdyn_steady_piolaversion/ZCurrent.sif @@ -10,7 +10,6 @@ Simulation Simulation Type = Steady Steady State Max Iterations = 1 ! Post File = zcurrent.ep - New Load Mesh = Logical True End Body 1 diff --git a/license_texts/ElmerCorporateCLA.pdf b/license_texts/ElmerCorporateCLA.pdf new file mode 100644 index 0000000000..41562272b1 Binary files /dev/null and b/license_texts/ElmerCorporateCLA.pdf differ diff --git a/license_texts/ElmerIndividualCLA.pdf b/license_texts/ElmerIndividualCLA.pdf new file mode 100644 index 0000000000..5e86245604 Binary files /dev/null and b/license_texts/ElmerIndividualCLA.pdf differ diff --git a/license_texts/ElmerLicensePolicy.txt b/license_texts/ElmerLicensePolicy.txt new file mode 100644 index 0000000000..f44e224769 --- /dev/null +++ b/license_texts/ElmerLicensePolicy.txt @@ -0,0 +1,50 @@ +Elmer licensing policy +====================== + +Elmer is a finite element code published under open source. It uses both the GPL (GNU General Public License, +v. 2.1) and LGPL license (GNU Lesser General Public License, v. 2.0). + +This is a short description of the licensing policy of Elmer suite from practical point of view. It applies to +the use, modification and contribution of the code. + + +Licences used +------------- +The code under LGPL license include the ElmerSolver main library (libelmersolver, codewise /fem/src/*.F90), +as well as the libraries matc and fhutiter. ElmerSolver library also depends on iso_varying_string that is +already published under LGPL. + +The parts of Elmer project still under the more restrictive GPL license include ElmerGUI, ElmerGrid, +and most of the existing physical modules a.k.a. solvers of Elmer (codewise /fem/src/modules/*). + +Upon building or running ElmerSolver may also utilize other libraries that are compatible with LGPL. These include Umfpack +(LGPL up to version 5.1.), Hypre (LGPL) and (P)Arpack (Free BSD). Note that if you build Elmer utilizing some more +limiting optional libraries you might not be able to use modules that have been distributed in a non-free fashion. + + +Using Elmer +----------- +If you’re just using Elmer then the open source licenses do not set any limitations for your work. However, if +linking other code with Elmer you should not combine viral licenses (e.g. GPL) and proprierity code. + + +Modifying Elmer +--------------- +Everybody has to freedom to modify the code for their own needs. However, if the modified code is distributed it must +be done under the very same license than the original code was under i.e. GPL modules stay under GPL even if modified by +the user. + + +Contributing to Elmer +--------------------- +Elmer project accepts contributions. If you want to contribute non-trivial contributions to the ElmerSolver library +or to the modules the you may sign an Contributor License Agreement (CLA) that grants certain rights to the main developer +of the code. The CA is based on Apache Contributor License Agreement widely accepted by the community. + +Some parts of the code is more relaxed when it comes to contributing. To be more specific, modules under "elmerice" and +"contrib" do not result to the need to sign a CA. + +Contact +------- +If you have further questions on the licensing of Elmer in your work or want to contribute to Elmer, please contact +elmeradm(at)csc.fi for more details. diff --git a/matc/src/files.c b/matc/src/files.c index 9964f1d03d..aa21fa648e 100755 --- a/matc/src/files.c +++ b/matc/src/files.c @@ -96,7 +96,9 @@ VARIABLE *fil_fread(var) VARIABLE *var; FILE *fp; int i, ind, len; + size_t iosize; + ind = *MATR(var); if (ind < 0 || ind >= MAXFILES) { @@ -120,7 +122,7 @@ VARIABLE *fil_fread(var) VARIABLE *var; error("fread: invalid length specified.\n"); } res = var_temp_new(TYPE_DOUBLE, 1, (len+sizeof(double)-1)>>3); - fread(MATR(res), 1, len, fp); + iosize = fread(MATR(res), 1, len, fp); if (feof(fp)) { @@ -239,7 +241,8 @@ VARIABLE *fil_fgets(var) VARIABLE *var; FILE *fp; int i, ind; - + char *ioptr; + ind = *MATR(var); if (ind < 0 || ind >= MAXFILES) { @@ -257,7 +260,7 @@ VARIABLE *fil_fgets(var) VARIABLE *var; error("fgets: end of file detected.\n"); } - fgets(str_pstr, STR_MAXLEN, fp); + ioptr = fgets(str_pstr, STR_MAXLEN, fp); if (feof(fp)) { @@ -518,7 +521,9 @@ VARIABLE *fil_load(ptr) VARIABLE *ptr; char *file; FILE *fp; - + size_t iosize; + int iostat; + file = var_to_string(ptr); if ((fp = fopen(file, "r")) == (FILE *)NULL) @@ -526,7 +531,7 @@ VARIABLE *fil_load(ptr) VARIABLE *ptr; error( "load: can't open file: %s.\n", file ); } - fscanf(fp, "%d %d %d %d", &ftype, &type, &nrow, &ncol); + iostat = fscanf(fp, "%d %d %d %d", &ftype, &type, &nrow, &ncol); if (ferror(fp)) { fclose(fp); error("load: error reading file.n"); @@ -539,7 +544,7 @@ VARIABLE *fil_load(ptr) VARIABLE *ptr; for(i = 0; i < nrow; i++) for(j = 0; j < ncol; j++) { - fscanf(fp, "%lf", &M(res, i, j)); + iostat = fscanf(fp, "%lf", &M(res, i, j)); if (ferror(fp)) { fclose(fp); error("load: error reading file.\n"); @@ -549,7 +554,7 @@ VARIABLE *fil_load(ptr) VARIABLE *ptr; else { fgetc(fp); - fread(MATR(res), 1, MATSIZE(res), fp); + iosize = fread(MATR(res), 1, MATSIZE(res), fp); if (ferror(fp)) { fclose(fp); error("load: error reading file.\n"); diff --git a/matc/src/main.c b/matc/src/main.c index d159013441..225e24215d 100644 --- a/matc/src/main.c +++ b/matc/src/main.c @@ -24,6 +24,7 @@ #include #include +#include #include "../config.h" #ifdef USE_READLINE @@ -45,7 +46,8 @@ int main( int argc, char **argv ) { char strt[2000]; char *str; - + char *ioptr; + #ifdef _OPENMP /* Set number of threads to 1, computations are single threaded anyway */ omp_set_num_threads(1); @@ -65,7 +67,7 @@ int main( int argc, char **argv ) add_history (str); #else - fgets( strt, 2000 , stdin); + ioptr = fgets( strt, 2000 , stdin); str = strt; #endif diff --git a/post/matc/src/elmer/matc.h b/post/matc/src/elmer/matc.h index 8e12438cae..00abddbd78 100644 --- a/post/matc/src/elmer/matc.h +++ b/post/matc/src/elmer/matc.h @@ -425,6 +425,29 @@ void error( char *format, ... ) longjmp( *jmpbuf, 2 ); } +void error_matc( char *format, ... ) +{// Just a copy of error( char *format, ...) to avoid undefined reference error + va_list args; + + va_start( args, format ); +#ifdef STRING_OUTPUT + if ( math_out_count+512 > math_out_allocated ) + { + math_out_allocated += 512; + math_out_str = (char *)realloc( math_out_str, math_out_allocated ); + } + math_out_count += sprintf( &math_out_str[math_out_count], "MATC ERROR: " ); + math_out_count += vsprintf( &math_out_str[math_out_count], format, args ); +#else + fprintf( math_err, "MATC ERROR: " ); + vfprintf( math_err, format, args ); +#endif + va_end( args ); + + (void)mem_free_all(); + longjmp( *jmpbuf, 2 ); +} + void PrintOut( char *format, ... ) { @@ -445,6 +468,7 @@ void PrintOut( char *format, ... ) } #else extern void error( char *format, ... ); +extern void error_matc( char *format, ... ); extern void PrintOut( char *format, ... ); #endif diff --git a/post/src/CMakeLists.txt b/post/src/CMakeLists.txt index 3d75883bcd..6bdd2c53b3 100644 --- a/post/src/CMakeLists.txt +++ b/post/src/CMakeLists.txt @@ -66,6 +66,7 @@ target_link_libraries(ElmerPost camera elements module objects visuals graphics glaux ${OPENGL_LIBRARIES} ${TCL_LIBRARY} ${TK_LIBRARY} matc m) IF(NOT(WIN32)) + target_link_libraries(ElmerPost X11) SET_TARGET_PROPERTIES(ElmerPost PROPERTIES INSTALL_RPATH "${ELMERSOLVER_RPATH_STRING}") ENDIF() @@ -84,7 +85,7 @@ IF(NOT(WIN32)) add_executable(QueryGLXExt ${QueryGLXExt_SRCS}) - target_link_libraries(QueryGLXExt ${OPENGL_LIBRARIES}) + target_link_libraries(QueryGLXExt ${OPENGL_LIBRARIES} X11) install(TARGETS QueryGLXExt DESTINATION bin) ENDIF(NOT(WIN32)) diff --git a/post/src/tcl/colorscale.tcl b/post/src/tcl/colorscale.tcl index 98750d4d4c..54ae3899ae 100755 --- a/post/src/tcl/colorscale.tcl +++ b/post/src/tcl/colorscale.tcl @@ -67,14 +67,14 @@ set ColorScaleColorMax 1.0 proc colscale_update {} { + .colscale.set.min delete 0 end + .colscale.set.max delete 0 end + global ColorScaleColor ColorScaleColorMin ColorScaleColorMax UpdateVariable ColorScaleColor - .colscale.set.min delete 0 end .colscale.set.min insert end [format %-10.5g $ColorScaleColorMin] - - .colscale.set.max delete 0 end .colscale.set.max insert end [format %-10.5g $ColorScaleColorMax] } diff --git a/post/src/tcl/isosurface.tcl b/post/src/tcl/isosurface.tcl index 00c380aacd..1653de1bea 100755 --- a/post/src/tcl/isosurface.tcl +++ b/post/src/tcl/isosurface.tcl @@ -76,14 +76,14 @@ set IsosurfaceColorSetMinMax 0 proc isosurface_update {} { + .isosurface.cset.min delete 0 end + .isosurface.cset.max delete 0 end + global IsosurfaceColor IsosurfaceColorMin IsosurfaceColorMax UpdateVariable IsosurfaceColor - .isosurface.cset.min delete 0 end .isosurface.cset.min insert end [format %-10.5g $IsosurfaceColorMin] - - .isosurface.cset.max delete 0 end .isosurface.cset.max insert end [format %-10.5g $IsosurfaceColorMax] } diff --git a/post/src/tcl/mesh.tcl b/post/src/tcl/mesh.tcl index 67d2573e8f..faf7ddc566 100755 --- a/post/src/tcl/mesh.tcl +++ b/post/src/tcl/mesh.tcl @@ -67,14 +67,14 @@ set NumberOfScalarVariables 1 proc mesh_update {} { + .mesh.set.min delete 0 end + .mesh.set.max delete 0 end + global MeshColor MeshColorMin MeshColorMax UpdateVariable MeshColor - .mesh.set.min delete 0 end .mesh.set.min insert end [format %-10.5g $MeshColorMin] - - .mesh.set.max delete 0 end .mesh.set.max insert end [format %-10.5g $MeshColorMax] }